Skip to content

API Overview

The Syneo/Barcoding API is built on LoopBack 4, a highly extensible Node.js and TypeScript framework for building APIs.

Architecture

The API follows a layered architecture:

┌─────────────────────────────────────────┐
│         HTTP Layer (REST)               │
│  - Controllers (606+)                   │
│  - Request validation                   │
│  - Response formatting                  │
└──────────────┬──────────────────────────┘

┌──────────────▼──────────────────────────┐
│        Business Logic Layer             │
│  - Services (22)                        │
│  - Business rules                       │
│  - External integrations                │
└──────────────┬──────────────────────────┘

┌──────────────▼──────────────────────────┐
│        Data Access Layer                │
│  - Repositories (582+)                  │
│  - Model definitions                    │
│  - Relations                            │
└──────────────┬──────────────────────────┘

┌──────────────▼──────────────────────────┐
│        Database Layer                   │
│  - MySQL connector                      │
│  - Connection pooling                   │
│  - Transaction management               │
└─────────────────────────────────────────┘

Key Features

OpenAPI Specification

The API automatically generates OpenAPI (Swagger) documentation:

Explorer URL: http://localhost:3001/explorer

The OpenAPI spec is used to:

  • Generate interactive API documentation
  • Auto-generate the @barcoding/sdk client
  • Validate requests and responses
  • Define API contracts

Dependency Injection

LoopBack 4 provides built-in dependency injection:

typescript
export class TicketController {
  constructor(
    @repository(TicketRepository)
    public ticketRepository: TicketRepository,
    @inject('services.NotificationService')
    protected notificationService: NotificationService,
  ) {}
}

JWT Authentication

The API uses JWT (JSON Web Tokens) for authentication:

typescript
@authenticate('jwt')
@get('/tickets')
async findTickets(): Promise<Ticket[]> {
  return this.ticketRepository.find();
}

Database Migrations

Schema changes are managed with Knex migrations:

bash
# Create migration
npx knex migrate:make add_status_to_tickets

# Run migrations
npm run migrate

Project Structure

api/b3api/
├── src/
│   ├── controllers/           # 606+ REST endpoints
│   │   ├── ticket.controller.ts
│   │   ├── user.controller.ts
│   │   └── ...
│   ├── repositories/          # 582+ data repositories
│   │   ├── ticket.repository.ts
│   │   ├── user.repository.ts
│   │   └── ...
│   ├── services/              # 22 business services
│   │   ├── notification.service.ts
│   │   ├── email.service.ts
│   │   └── ...
│   ├── models/                # Data models
│   │   ├── ticket.model.ts
│   │   ├── user.model.ts
│   │   └── ...
│   ├── datasources/           # Database connections
│   │   └── mysqldb01.datasource.ts
│   ├── knex_migrations/       # Database migrations
│   │   ├── 20240101000000_initial.ts
│   │   └── ...
│   ├── application.ts         # LoopBack application
│   ├── index.ts               # Entry point
│   └── sequence.ts            # Request sequence
├── dist/                      # Compiled output
├── package.json
└── tsconfig.json

API Statistics

  • Controllers: 606+
  • Repositories: 582+
  • Services: 22
  • Models: 500+
  • Datasources: 1 (MySQL)

Development Workflow

1. Start the API

bash
cd api/b3api
npm run build
npm run start

API will be available at:

  • API Base: http://localhost:3001
  • Explorer: http://localhost:3001/explorer
  • OpenAPI Spec: http://localhost:3001/openapi.json

2. Make Changes

Edit files in src/:

typescript
// src/controllers/ticket.controller.ts
@post('/tickets')
async createTicket(@requestBody() ticket: Ticket): Promise<Ticket> {
  return this.ticketRepository.create(ticket);
}

3. Rebuild

bash
npm run build
# Or watch mode
npm run build:watch

4. Test

bash
# Run tests
npm run test

# Lint
npm run lint

5. Update SDK

After API changes:

bash
cd web
yarn build-sdk

Configuration

Database Connection

Configuration in src/datasources/mysqldb01.datasource.config.json:

json
{
  "name": "mysqldb01",
  "connector": "mysql",
  "host": "mysqldb",
  "port": 3306,
  "user": "root",
  "password": "Ypp01o#3",
  "database": "lb4",
  "multipleStatements": true
}

WARNING

Development credentials only. Never use in production.

Application Configuration

The API loads configuration from:

  1. Environment variables
  2. Azure KeyVault (production)
  3. Default configuration files

Common Tasks

Create a New Controller

bash
lb4 controller
? Controller class name: Product
? What kind of controller would you like to generate? REST Controller with CRUD functions
? What is the name of the model to use with this CRUD repository? Product
? What is the name of your CRUD repository? ProductRepository
? What is the type of your ID? number
? What is the base HTTP path name of the CRUD operations? /products

Create a New Model

bash
lb4 model
? Model class name: Product
? Please select the model base class: Entity
? Allow additional properties? No

Create a New Repository

bash
lb4 repository
? Please select the datasource: Mysqldb01Datasource
? Select the model(s) you want to generate a repository for: Product

Create a New Service

bash
lb4 service
? Service type: proxy
? Please enter the name of the service: EmailService
? Please enter the name of the datasource: EmailDatasource

Testing

Unit Tests

bash
npm run test

Manual Testing with Explorer

  1. Open http://localhost:3001/explorer
  2. Select an endpoint
  3. Click "Try it out"
  4. Fill in parameters
  5. Execute

Automated API Tests

Tests use Mocha and the ApiTestHelper:

typescript
describe('TicketController', () => {
  let app: B3ApiApplication;
  let client: Client;

  before(async () => {
    app = await setupApplication();
    client = createRestAppClient(app);
  });

  after(async () => {
    await app.stop();
  });

  it('should create a ticket', async () => {
    const ticket = { title: 'Test', status: 'open' };
    const response = await client
      .post('/tickets')
      .send(ticket)
      .expect(200);
    expect(response.body).to.have.property('id');
  });
});

Next Steps

Syneo/Barcoding Documentation