REST API Design with Genspark: Fully Automated from OpenAPI Specification to Implementation

Introduction: The Importance of API Design

REST APIs are **the most crucial interface** in modern web applications. Poor design can lead to difficulties in frontend development, performance issues, and decreased maintainability. APIs that disregard RESTful principles may require extensive revisions later.

By utilizing Genspark for API design, we can now consistently generate **OpenAPI specifications, endpoint implementations, documentation, and test code**.

Important Recommendation 1: Genspark has a **perfect understanding of RESTful design principles**. It proposes API designs that adhere to best practices, including a resource-oriented approach, appropriate use of HTTP methods, status codes, and error handling.

Non-RESTful APIs Resulting from Manual Design

When designing APIs manually in the early stages of a project, we encountered the following issues:

Examples of Design Mistakes

  • GET /api/deleteUser/:id - Deletion operation with GET method
  • POST /api/getUserData - Verb-based endpoint
  • ❌ All errors returning 200 OK
  • ❌ Inconsistent response formats across endpoints
  • ❌ No pagination implemented
  • ❌ No API documentation exists

As a result of proceeding with development while having these issues, confusion arose on the frontend, becoming a breeding ground for bugs.

Example 1: Automated OpenAPI Specification Generation

Here's an example where we asked Genspark to design the API for a blog system.

Request to Genspark

Please design a REST API for a blog system with the following requirements:

## Requirements
- Article CRUD
- Category management
- Comment functionality
- Like functionality
- Article search

## Constraints
- Adhere to RESTful design
- Output in OpenAPI 3.0 format
- Authentication is JWT Bearer
- Pagination required
- Unified error responses

## Output Format
OpenAPI Specification (YAML format)

Genspark generated a perfect OpenAPI specification (excerpt).

Generated OpenAPI Specification (Excerpt)

openapi: 3.0.0
info:
  title: Blog API
  version: 1.0.0
  description: RESTful blog system API

servers:
  - url: https://api.example.com/v1
    description: Production server

paths:
  /articles:
    get:
      summary: Get all articles
      tags: [Articles]
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
        - name: category
          in: query
          schema:
            type: string
        - name: search
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Article'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
    
    post:
      summary: Create a new article
      tags: [Articles]
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateArticleRequest'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Article'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /articles/{id}:
    get:
      summary: Get article by ID
      tags: [Articles]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Article'
        '404':
          $ref: '#/components/responses/NotFound'
    
    put:
      summary: Update article
      tags: [Articles]
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateArticleRequest'
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/Article'
        '404':
          $ref: '#/components/responses/NotFound'
    
    delete:
      summary: Delete article
      tags: [Articles]
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '204':
          description: No content
        '404':
          $ref: '#/components/responses/NotFound'

  /articles/{id}/like:
    post:
      summary: Like an article
      tags: [Articles]
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      likes:
                        type: integer

components:
  schemas:
    Article:
      type: object
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        slug:
          type: string
        content:
          type: string
        categoryId:
          type: string
          format: uuid
        authorId:
          type: string
          format: uuid
        likes:
          type: integer
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    
    Pagination:
      type: object
      properties:
        page:
          type: integer
        limit:
          type: integer
        total:
          type: integer
        totalPages:
          type: integer
    
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
            details:
              type: array
              items:
                type: object

  responses:
    BadRequest:
      description: Bad request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    
    Unauthorized:
      description: Unauthorized
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    
    NotFound:
      description: Not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

Strengths of the OpenAPI Specification

  • ✅ **RESTful Design:** Resource-oriented, appropriate HTTP method usage
  • ✅ **Unified Response Format:** Consistency across all endpoints
  • ✅ **Error Handling:** Appropriate status codes and error schemas
  • ✅ **Pagination:** Support for large data volumes
  • ✅ **Authentication/Authorization:** Security scheme definition
Important Recommendation 2: The OpenAPI specification is a **"design document," "documentation," and "contract."** By having Genspark generate it, you can prevent discrepancies between frontend and backend. Combining it with database design makes it even more powerful.

Example 2: Full Automation of Endpoint Implementation

Once the OpenAPI specification is finalized, the next step is implementation. We asked Genspark to "implement Express endpoints based on this OpenAPI specification."

Generated Endpoint Implementation (Excerpt)

// src/routes/articles.routes.js

const express = require('express');
const router = express.Router();
const articlesController = require('../controllers/articles.controller');
const authMiddleware = require('../middleware/auth');
const validateRequest = require('../middleware/validateRequest');
const { articleSchemas } = require('../schemas/article.schemas');

// GET /articles - Get article list
router.get('/',
  validateRequest(articleSchemas.getArticles),
  articlesController.getArticles
);

// POST /articles - Create article
router.post('/',
  authMiddleware,
  validateRequest(articleSchemas.createArticle),
  articlesController.createArticle
);

// GET /articles/:id - Get article details
router.get('/:id',
  validateRequest(articleSchemas.getArticleById),
  articlesController.getArticleById
);

// PUT /articles/:id - Update article
router.put('/:id',
  authMiddleware,
  validateRequest(articleSchemas.updateArticle),
  articlesController.updateArticle
);

// DELETE /articles/:id - Delete article
router.delete('/:id',
  authMiddleware,
  articlesController.deleteArticle
);

// POST /articles/:id/like - Like
router.post('/:id/like',
  authMiddleware,
  articlesController.likeArticle
);

module.exports = router;

// src/controllers/articles.controller.js

const articlesService = require('../services/articles.service');
const { NotFoundError, BadRequestError } = require('../utils/errors');

exports.getArticles = async (req, res, next) => {
  try {
    const { page = 1, limit = 20, category, search } = req.query;
    
    const result = await articlesService.getArticles({
      page: parseInt(page),
      limit: parseInt(limit),
      category,
      search
    });
    
    res.json({
      data: result.articles,
      pagination: {
        page: result.page,
        limit: result.limit,
        total: result.total,
        totalPages: Math.ceil(result.total / result.limit)
      }
    });
  } catch (error) {
    next(error);
  }
};

exports.createArticle = async (req, res, next) => {
  try {
    const userId = req.user.id;  // authMiddleware sets this
    const articleData = {
      ...req.body,
      authorId: userId
    };
    
    const article = await articlesService.createArticle(articleData);
    
    res.status(201).json({ data: article });
  } catch (error) {
    next(error);
  }
};

exports.getArticleById = async (req, res, next) => {
  try {
    const { id } = req.params;
    
    const article = await articlesService.getArticleById(id);
    
    if (!article) {
      throw new NotFoundError('Article not found');
    }
    
    res.json({ data: article });
  } catch (error) {
    next(error);
  }
};

exports.updateArticle = async (req, res, next) => {
  try {
    const { id } = req.params;
    const userId = req.user.id;
    
    // Permission check
    const article = await articlesService.getArticleById(id);
    if (!article) {
      throw new NotFoundError('Article not found');
    }
    if (article.authorId !== userId && req.user.role !== 'admin') {
      throw new ForbiddenError('You can only update your own articles');
    }
    
    const updatedArticle = await articlesService.updateArticle(id, req.body);
    
    res.json({ data: updatedArticle });
  } catch (error) {
    next(error);
  }
};

exports.deleteArticle = async (req, res, next) => {
  try {
    const { id } = req.params;
    const userId = req.user.id;
    
    // Permission check
    const article = await articlesService.getArticleById(id);
    if (!article) {
      throw new NotFoundError('Article not found');
    }
    if (article.authorId !== userId && req.user.role !== 'admin') {
      throw new ForbiddenError('You can only delete your own articles');
    }
    
    await articlesService.deleteArticle(id);
    
    res.status(204).send();
  } catch (error) {
    next(error);
  }
};

exports.likeArticle = async (req, res, next) => {
  try {
    const { id } = req.params;
    const userId = req.user.id;
    
    const likes = await articlesService.likeArticle(id, userId);
    
    res.json({ data: { likes } });
  } catch (error) {
    next(error);
  }
};

Strengths of the Generated Code

  • ✅ **Layer Separation:** Clear separation of routes, controllers, and services
  • ✅ **Appropriate Error Handling:** Use of custom error classes
  • ✅ **Permission Check:** Only own articles can be edited
  • ✅ **Validation:** Request schema validation
  • ✅ **Unified Response Format:** Consistency across all endpoints
Important Recommendation 3: Genspark can **fully automate code generation from the OpenAPI specification**. This eliminates discrepancies between design and implementation, ensuring code always matches the specification. Combining it with a testing strategy further improves quality.

Example 3: API Documentation and Test Code Generation

From the OpenAPI specification, we also had Swagger documentation and test code automatically generated.

1. Swagger UI Setup

// src/app.js

const express = require('express');
const swaggerUi = require('swagger-ui-express');
const YAML = require('yamljs');
const swaggerDocument = YAML.load('./openapi.yaml');

const app = express();

// Swagger UI
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));

// ...other middleware configurations

module.exports = app;

This allows interactive API documentation to be viewed at http://localhost:3000/api-docs.

2. Automated Test Code Generation

// tests/articles.test.js

const request = require('supertest');
const app = require('../src/app');
const { createTestUser, generateToken } = require('./helpers');

describe('Articles API', () => {
  let authToken;
  let userId;
  
  beforeAll(async () => {
    const user = await createTestUser();
    userId = user.id;
    authToken = generateToken(user);
  });
  
  describe('GET /articles', () => {
    it('should return paginated articles', async () => {
      const res = await request(app)
        .get('/articles?page=1&limit=10')
        .expect(200);
      
      expect(res.body).toHaveProperty('data');
      expect(res.body).toHaveProperty('pagination');
      expect(Array.isArray(res.body.data)).toBe(true);
      expect(res.body.pagination).toMatchObject({
        page: 1,
        limit: 10,
        total: expect.any(Number),
        totalPages: expect.any(Number)
      });
    });
    
    it('should filter by category', async () => {
      const res = await request(app)
        .get('/articles?category=technology')
        .expect(200);
      
      res.body.data.forEach(article => {
        expect(article.category).toBe('technology');
      });
    });
  });
  
  describe('POST /articles', () => {
    it('should create a new article', async () => {
      const newArticle = {
        title: 'Test Article',
        content: 'This is a test article content.',
        categoryId: 'some-category-id'
      };
      
      const res = await request(app)
        .post('/articles')
        .set('Authorization', `Bearer ${authToken}`)
        .send(newArticle)
        .expect(201);
      
      expect(res.body.data).toMatchObject({
        id: expect.any(String),
        title: newArticle.title,
        content: newArticle.content,
        authorId: userId
      });
    });
    
    it('should return 401 without auth token', async () => {
      const res = await request(app)
        .post('/articles')
        .send({ title: 'Test' })
        .expect(401);
    });
  });
  
  describe('PUT /articles/:id', () => {
    it('should update own article', async () => {
      const article = await createTestArticle(userId);
      
      const updatedData = {
        title: 'Updated Title'
      };
      
      const res = await request(app)
        .put(`/articles/${article.id}`)
        .set('Authorization', `Bearer ${authToken}`)
        .send(updatedData)
        .expect(200);
      
      expect(res.body.data.title).toBe(updatedData.title);
    });
    
    it('should return 403 when updating others article', async () => {
      const otherUser = await createTestUser();
      const article = await createTestArticle(otherUser.id);
      
      await request(app)
        .put(`/articles/${article.id}`)
        .set('Authorization', `Bearer ${authToken}`)
        .send({ title: 'Hacked' })
        .expect(403);
    });
  });
});
Important Recommendation 4: With an OpenAPI specification, **documentation and test code can also be automatically generated**. Once the specification is defined, implementation, documentation, and tests are all generated consistently.

Complete API Design Workflow

Here's a summary of the complete API design workflow utilizing Genspark.

Step 1: Requirements Definition

  1. List required endpoints
  2. Organize resource structure
  3. Clarify authentication and authorization requirements

Step 2: OpenAPI Specification Generation

Request to Genspark:
"Please create an OpenAPI 3.0 specification with the following requirements"
+ Detailed list of requirements

Step 3: Specification Review and Revision

  1. Review the generated specification with the team
  2. Request revisions from Genspark as needed
  3. Reach agreement with the frontend team

Step 4: Implementation Generation

Request to Genspark:
"Please implement Express endpoints based on this OpenAPI specification"
+ Technology stack (Express, Prisma, etc.)

Step 5: Test Code Generation

Request to Genspark:
"Please generate Jest test code based on this OpenAPI specification"

Step 6: Documentation Publication

  1. Set up Swagger UI
  2. Publish API documentation
  3. Share with the frontend team
Important Recommendation 5: Consistency across **"Specification → Implementation → Testing → Documentation"** is vital for API design. With Genspark, everything can be automatically generated starting from the OpenAPI specification, ensuring consistency.

Summary: A New Era of AI and API Design

Genspark **consistently automates API design from specification to implementation, testing, and documentation**. A development flow centered around the OpenAPI specification has smoothed coordination between frontend and backend.

Outcomes of Genspark Utilization

  • API Design Time: 2 days → half a day (4x faster)
  • Implementation Time: 1 week → 2 days (3.5x faster)
  • Specification-Implementation Discrepancy: Zero
  • Documentation: Always up-to-date

Actions You Can Start Today

  • ✅ Generate OpenAPI specifications for existing APIs
  • ✅ Design new APIs with an OpenAPI-first approach
  • ✅ Publish documentation with Swagger UI
  • ✅ Automatically generate test code

API design is also entering a new era of collaboration with AI.

Reference Links