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
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. It mentions what the tool generates but doesn't describe how it works: whether it creates files, returns code snippets, requires specific permissions, has rate limits, or what the output format looks like. For a code generation tool with no annotation coverage, this leaves significant behavioral questions unanswered.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise - a single sentence that efficiently communicates the core functionality. Every word earns its place: 'Generate TypeScript backend code' (action), 'for Arbitrum dApps' (context), 'Supports NestJS and Express' (framework options), 'with viem integration' (key technology). No wasted words or redundant information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a code generation tool with 6 parameters, no annotations, and no output schema, the description is insufficiently complete. It doesn't explain what the tool actually produces (files? code blocks? project structure?), doesn't mention any constraints or requirements, and provides minimal context about how the generation works. The description alone doesn't give enough information for an agent to understand the tool's full behavior.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 100% schema description coverage, the baseline is 3. The description adds minimal parameter context beyond the schema - it mentions 'Supports NestJS and Express with viem integration' which relates to the framework parameter, but doesn't provide additional semantic meaning for other parameters like contract_abi or template. The description doesn't compensate for any gaps since there are none in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Generate TypeScript backend code for Arbitrum dApps' with specific frameworks mentioned (NestJS and Express) and viem integration. It distinguishes from some siblings like generate_frontend or generate_tests, but doesn't explicitly differentiate from other backend-related tools like generate_messaging_code or generate_indexer.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. With many sibling tools available (like generate_bridge_code, generate_messaging_code, generate_oracle, etc.), there's no indication of when backend code generation is appropriate versus specialized code generation tools. No prerequisites or exclusions are mentioned.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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