Skip to main content
Glama

generate_backend

Generate TypeScript backend code for Arbitrum dApps using NestJS or Express frameworks with viem integration for blockchain interaction.

Instructions

Generate TypeScript backend code for Arbitrum dApps. Supports NestJS and Express with viem integration.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
promptYesDescription of the backend functionality needed
frameworkNoBackend framework to usenestjs
templateNoSpecific template to use (auto-selected if not provided)
contract_abiNoContract ABI JSON string (optional)
contract_addressNoContract address to integrate with
include_testsNoInclude test files

Implementation Reference

  • The `GenerateBackendTool` class handles the backend generation logic, including template selection, template customization based on ABIs and prompts, and generating configuration/test files.
    class GenerateBackendTool(BaseTool):
        """Generate backend code for dApps with Web3 integration."""
    
        name = "generate_backend"
        description = """Generate TypeScript backend code for Arbitrum dApps.
    
    Supports:
    - NestJS with Stylus contract integration (viem)
    - Express with Stylus contract integration (lightweight)
    - NestJS with GraphQL for subgraph querying
    - API Gateway for cross-chain L1/L2/L3 operations
    
    The generated code includes controllers, services, and configuration files."""
    
        input_schema = {
            "type": "object",
            "properties": {
                "prompt": {
                    "type": "string",
                    "description": "Description of the backend functionality needed",
                },
                "framework": {
                    "type": "string",
                    "enum": ["nestjs", "express"],
                    "description": "Backend framework to use",
                    "default": "nestjs",
                },
                "template": {
                    "type": "string",
                    "enum": ["nestjs_stylus", "express_stylus", "nestjs_graphql", "api_gateway"],
                    "description": "Specific template to use (optional, auto-selected if not provided)",
                },
                "contract_abi": {
                    "type": "string",
                    "description": "Contract ABI JSON string (optional, uses default if not provided)",
                },
                "contract_address": {
                    "type": "string",
                    "description": "Contract address to integrate with",
                },
                "include_tests": {
                    "type": "boolean",
                    "description": "Include test files",
                    "default": False,
                },
            },
            "required": ["prompt"],
        }
    
        def __init__(self, vectordb=None):
            """Initialize with optional vector database for context."""
            super().__init__()
            self.vectordb = vectordb
    
        def execute(self, **kwargs) -> dict[str, Any]:
            """Generate backend code based on the request."""
            prompt = kwargs.get("prompt", "")
            framework = kwargs.get("framework", "nestjs")
            template_name = kwargs.get("template")
            contract_abi = kwargs.get("contract_abi")
            contract_address = kwargs.get("contract_address", "0x...")
            include_tests = kwargs.get("include_tests", False)
    
            # Validate inputs
            if not prompt:
                return {"error": "prompt is required"}
    
            # Select template
            if template_name:
                template = get_backend_template(template_name)
                if not template:
                    return {"error": f"Unknown template: {template_name}"}
            else:
                template = select_backend_template(framework, prompt)
    
            # Context retrieval (template-based generation doesn't require RAG)
            context = []
    
            # Customize template based on prompt
            files = self._customize_template(template, prompt, contract_abi, contract_address)
    
            # Generate package.json
            package_json = self._generate_package_json(template, prompt)
    
            # Add test files if requested
            if include_tests:
                test_files = self._generate_tests(template, prompt)
                files.update(test_files)
    
            # Build response
            result = {
                "template_used": template.name,
                "framework": template.framework,
                "files": files,
                "package_json": package_json,
                "dependencies": template.dependencies,
                "dev_dependencies": template.dev_dependencies,
                "env_vars": template.env_vars,
                "scripts": template.scripts,
                "setup_instructions": self._get_setup_instructions(template),
            }
    
            if context:
                result["references"] = [
                    {
                        "source": c.get("metadata", {}).get("source", "Unknown"),
                        "relevance": c.get("distance", 0),
                    }
                    for c in context[:3]
                ]
    
            return result
    
        def _customize_template(
            self,
            template: BackendTemplate,
            prompt: str,
            contract_abi: Optional[str],
            contract_address: str,
        ) -> dict[str, str]:
            """Customize template files based on user requirements."""
            files = dict(template.files)
    
            # Replace placeholder contract address
            for path, content in files.items():
                if "0x0000000000000000000000000000000000000000" in content:
                    files[path] = content.replace(
                        "0x0000000000000000000000000000000000000000",
                        contract_address,
                    )
                if "0x..." in content:
                    files[path] = content.replace("0x...", contract_address)
    
            # If custom ABI provided, update contract configuration
            if contract_abi:
                try:
                    abi = json.loads(contract_abi)
                    abi_content = self._generate_abi_file(abi)
    
                    # Add or update ABI configuration based on framework
                    if template.framework == "nestjs":
                        files["src/contract/contract.abi.ts"] = abi_content
                    else:
                        files["src/config/contract.abi.ts"] = abi_content
    
                    # Build human-readable ABI for placeholder replacement
                    hr_abi = self._build_human_readable_abi(abi)
    
                    # Replace __ABI_PLACEHOLDER__ in template files
                    files = render_with_abi(files, abi, hr_abi)
    
                    # Generate ABI-aware service methods/routes
                    service_code = generate_service_from_abi(hr_abi, template.framework)
                    if service_code:
                        self._inject_abi_methods(files, service_code, template.framework)
    
                except json.JSONDecodeError:
                    pass  # Use default ABI
    
            return files
    
        @staticmethod
        def _build_human_readable_abi(abi: list) -> list[str]:
            """Convert JSON ABI to human-readable format for generate_service_from_abi."""
            hr = []
            for item in abi:
                if item.get("type") != "function":
                    continue
                name = item.get("name", "")
                inputs = ", ".join(
                    f"{inp.get('type', '')} {inp.get('name', '')}"
                    for inp in item.get("inputs", [])
                )
                outputs = ", ".join(
                    inp.get("type", "") for inp in item.get("outputs", [])
                )
                mutability = item.get("stateMutability", "nonpayable")
                sig = f"function {name}({inputs})"
                if mutability in ("view", "pure"):
                    sig += f" {mutability}"
                if outputs:
                    sig += f" returns ({outputs})"
                hr.append(sig)
            return hr
    
        @staticmethod
        def _inject_abi_methods(
            files: dict[str, str], service_code: str, framework: str
        ) -> None:
            """Inject ABI-aware methods into the service file, replacing generic placeholders."""
            if framework == "nestjs":
                key = "src/contract/contract.service.ts"
                if key in files:
                    content = files[key]
                    # Replace the generic getBalance method with ABI-aware methods
                    marker = "  // Add your contract methods here"
                    generic_method = (
                        "  async getBalance(address: string): Promise<bigint> {\n"
                        "    const client = this.web3Service.getPublicClient();\n"
                        "    return client.getBalance({ address: address as `0x${string}` });\n"
                        "  }"
                    )
                    if marker in content:
                        content = content.replace(
                            marker + "\n" + generic_method,
                            "  // ABI-generated methods\n" + service_code,
                        )
                        # Uncomment the ABI import and contract initialization
                        content = content.replace(
                            "// import { abi } from './abi';",
                            "import { CONTRACT_ABI } from './contract.abi';",
                        )
                        content = content.replace(
                            "      // Initialize contract instance\n"
                            "      // this.contract = getContract({\n"
                            "      //   address: contractAddress as `0x${string}`,\n"
                            "      //   abi,\n"
                            "      //   client: this.web3Service.getPublicClient(),\n"
                            "      // });",
                            "      this.contract = getContract({\n"
                            "        address: contractAddress as `0x${string}`,\n"
                            "        abi: CONTRACT_ABI,\n"
                            "        client: this.web3Service.getPublicClient(),\n"
                            "      });",
                        )
                        files[key] = content
            else:
                key = "src/routes/contract.ts"
                if key in files:
                    # For Express, replace the entire route content with ABI-aware routes
                    files[key] = (
                        "import { Router } from 'express';\n"
                        "import { publicClient } from '../index';\n\n"
                        "const router = Router();\n\n"
                        + service_code
                        + "\n\nexport default router;\n"
                    )
    
        def _generate_abi_file(self, abi: list) -> str:
            """Generate TypeScript ABI file from JSON ABI."""
            abi_json = json.dumps(abi, indent=2)
            return f'''import {{ parseAbi }} from 'viem';
    
    // Contract ABI
    export const CONTRACT_ABI = {abi_json} as const;
    
    // For viem usage, parse the ABI
    export const PARSED_ABI = parseAbi([
      // Add function signatures here
    ]);
    '''
    
        def _generate_package_json(self, template: BackendTemplate, prompt: str) -> dict:
            """Generate package.json content."""
            # Derive package name from prompt
            words = prompt.lower().split()[:3]
            name = "-".join(w for w in words if w.isalnum())[:20] or "dapp-backend"
    
            return {
                "name": name,
                "version": "0.1.0",
                "description": f"Backend for: {prompt[:100]}",
                "scripts": template.scripts,
                "dependencies": template.dependencies,
                "devDependencies": template.dev_dependencies,
            }
    
        def _generate_tests(self, template: BackendTemplate, prompt: str) -> dict[str, str]:
            """Generate test files for the backend."""
            tests = {}
    
            if template.framework == "nestjs":
                tests["test/contract.e2e-spec.ts"] = '''import { Test, TestingModule } from '@nestjs/testing';
    import { INestApplication } from '@nestjs/common';
    import * as request from 'supertest';
    import { AppModule } from '../src/app.module';
    
    describe('ContractController (e2e)', () => {
      let app: INestApplication;
    
      beforeEach(async () => {
        const moduleFixture: TestingModule = await Test.createTestingModule({
          imports: [AppModule],
        }).compile();
    
        app = moduleFixture.createNestApplication();
        await app.init();
      });
    
      afterAll(async () => {
        await app.close();
      });
    
      it('/health (GET)', () => {
        return request(app.getHttpServer())
          .get('/health')
          .expect(200)
          .expect((res) => {
            expect(res.body.status).toBe('ok');
          });
      });
    
      it('/contract/number (GET)', () => {
        return request(app.getHttpServer())
          .get('/contract/number')
          .expect(200)
          .expect((res) => {
            expect(res.body).toHaveProperty('number');
          });
      });
    });
    '''
                tests["test/jest-e2e.json"] = '''{
      "moduleFileExtensions": ["js", "json", "ts"],
      "rootDir": ".",
      "testEnvironment": "node",
      "testRegex": ".e2e-spec.ts$",
      "transform": {
        "^.+\\\\.(t|j)s$": "ts-jest"
      }
    }
    '''
    
            elif template.framework == "express":
                tests["test/contract.test.ts"] = '''import request from 'supertest';
    import express from 'express';
    import { contractRouter } from '../src/routes/contract';
    import { healthRouter } from '../src/routes/health';
    
    const app = express();
    app.use(express.json());
    app.use('/health', healthRouter);
    app.use('/contract', contractRouter);
    
    describe('API Tests', () => {
      describe('GET /health', () => {
        it('should return status ok', async () => {
          const res = await request(app).get('/health');
          expect(res.status).toBe(200);
          expect(res.body.status).toBe('ok');
        });
      });
    
      describe('GET /contract/number', () => {
        it('should return a number', async () => {
          const res = await request(app).get('/contract/number');
          expect(res.status).toBe(200);
          expect(res.body).toHaveProperty('number');
        });
      });
    });
    '''
    
            return tests
    
        def _get_setup_instructions(self, template: BackendTemplate) -> list[str]:
            """Get setup instructions for the template."""
            instructions = [
                "1. Create a new directory and copy the generated files",
                "2. Copy .env.example to .env and fill in the values",
                "3. Run: npm install",
            ]
    
            if template.framework == "nestjs":
                instructions.extend([
                    "4. Run in development: npm run start:dev",
                    "5. Build for production: npm run build",
                    "6. Run in production: npm run start:prod",
                ])
            else:
                instructions.extend([
                    "4. Run in development: npm run dev",
                    "5. Build: npm run build",
                    "6. Run in production: npm start",
                ])
    
            return instructions

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Quantum3-Labs/ARBuilder'

If you have feedback or need assistance with the MCP directory API, please join our Discord server