Define the API from code-first approach

You may want to build your application from the ‘bottom up’ if you:

  • do not have a complete understanding of what your existing tools can offer.
  • want to capture already existing domain models so that they can be reflectedas APIs for external consumption.
  • need to grow and change your API from the initial implementation
  • want to set up and run an API from an early stage of the production to easilyenvision the big picture of the end product.There are various tools available to LoopBack which allows this bottom-upapproach of building your application to be simple through the usages ofmetadata and decorators.

Start with LoopBack artifacts

With TypeScript’sexperimental decoratorfeature, APIs can be automatically built and exposed as your applicationcontinues development. Some key concepts utilize decorators to gather _metadata_about your code and then assemble them into a valid OpenAPI specification, whichprovide a description of your API. These concepts and their decorators include:

  • Model
    • @model()
    • @property()
  • Routes
    • @operation()
    • @param()

Define your models

Your models act as common definitions between data being handled by the APIlayer and the datasource layer. Since your API is going to be built around themanipulation of models and their properties, they will be the first to bedefined.

Note:

Todo model from tutorialis used for demonstration here.

First, write a simple TypeScript class describing your model and its properties:

src/models/todo.model.ts

  1. export class Todo {
  2. id?: number;
  3. title: string;
  4. desc?: string;
  5. isComplete: boolean;
  6. }

To this representation of your model, we can use the @model and @propertydecorators to create the model’s metadata; a model definition. LoopBack andLoopBack extensions can use this model definition for a wide variety of uses,such as:

  • generating OpenAPI schema for your APIs
  • validating instances of the models during the request/response lifecycle
  • automatically inferring relationships between models during datasourceoperationsTo apply these decorators to your model, you simply prefix the class definitionwith the @model decorator, and prefix each property with the @propertydecorator:

src/models/todo.model.ts

  1. import {model, property} from '@loopback/repository';
  2. @model()
  3. export class Todo {
  4. @property()
  5. id?: number;
  6. @property({
  7. required: true,
  8. })
  9. title: string;
  10. @property()
  11. desc?: string;
  12. @property()
  13. isComplete: boolean;
  14. }

Define your routes

Note:

TodoController from tutorialis used for demonstration here.

Once your models are defined, create a controller to host your routes for eachpaths of your API:

src/controllers/todo.controller.ts

  1. import {Todo} from '../models/todo.model';
  2. export class TodoController {
  3. constructor() {}
  4. async createTodo(todo: Todo) {
  5. // data creating logic goes here
  6. }
  7. async findTodoById(id: number, items?: boolean): Promise<Todo> {
  8. // data retrieving logic goes here
  9. }
  10. // ...
  11. }

The controller’s routes in their current state has no information on which APIendpoints they belong to. Add them in by appending @operation to each methodof your routes and @param or @requestBody to its parameters:

src/controllers/todo.controller.ts

  1. import {Todo} from '../models/todo.model';
  2. import {post, get, param, requestBody} from '@loopback/rest';
  3. export class TodoController {
  4. constructor() {}
  5. @post('/todos') // same as @operation('post', '/todos');
  6. async createTodo(@requestBody() todo: Todo) {
  7. // data creating logic goes here
  8. }
  9. @get('/todos/{id}')
  10. async findTodoById(
  11. @param.path.number('id') id: number,
  12. @param.query.boolean('items') items?: boolean,
  13. ): Promise<Todo> {
  14. // data retrieving logic goes here
  15. }
  16. // ...
  17. }

Once your routes have been decorated, your application is ready to serve itsAPI. When an instance of RestServer is run, an OpenAPI specificationrepresenting your application’s API is built. The spec is generated entirelyfrom the decorated elements’ metadata, which in turn provides routing logic foryour API when your application is running.

Reviewing your API specification

To review your complete API specification, run your application with thedecorated controllers registered. Once it is running, visit /openapi.jsonendpoint to access your API specification in JSON format or /openapi.yaml forYAML. Alternatively, the specification file can also be accessed in code throughthe getApiSpec() function from your RestServer instance.

For a complete walkthrough of developing an application with the bottom-upapproach, see ourTodo applicationtutorial.

Next: Defining your testing strategy