Giter VIP home page Giter VIP logo

petstore-client's Introduction

PetStore API Client

Implementation details

  • The design has been guided all by tests using TDD
    • Most of the time I used Obvious Implementation, although sometimes I used Fake it, til you make it with Triangulation and TCR (test && commit || revert) when complexity grew and baby steps were uncertain (specially happened with the content negotiation feature).
    • To find the simplest possible implementation reducing Accidental Complication and focusing myself on the Developer Experience (DX from now on) with the client, I always used test-first and most of the time in combination with Assert-First except when tests were copy&pasted, like the error handling use case.
    • I used Outside-In TDD, to help me design the API client from a user standpoint. And the usage of test doubles has been very limited. I usually tend to avoid mocking frameworks like the one Jest or other frameworks provide, because they usually make your tests very tighly coupled to the code and this can lead very easily to serious testing issues like Fragile Tests. So I treat test doubles as production code and changes to this classes are made at the same pace as production code to avoid fragile tests.
  • Nearly 100% code coverage.
  • I used the package node-fetch as a fetch implementation, since the fetch API in NodeJS is still in an experimental phase.
  • In order to make the client simpler both at code level and at DX level, this client only supports JSON responses. At first, I implemented a very basic content negotiation. But almost inmediately I saw it was not worth the effort for two reasons: 1) It made the code extremely complex for 2) no benefit for the users of the client, since they would have to prepare their code for 2 different response models (the translation to an object from an XML response is different from a JSON response) having exactly the same information, increasing that way the complexity of their code. So for the sake of simplicity and to improve DX I get rid of the feature. Anyway, I created a branch (with-content-negotiation) in case you want to see how I have attempted to implement content negotiation.
  • Easy error handling. Clients can catch several types of errors / exceptions ๐Ÿ‘‡
  • Responses are validated both at compile-time and at runtime using Zod.
  • Scalability by design
    • Dependency Injection & Dependency Inversion Principle. I used dependency injection in the Petstore class to make the code loose coupled and able to be used in nearly all environments with the Transport abstraction. Dependency Inversion Principle have been used to make the code as general as possible and to not depend on concret implementations like fetch and depend on more high level abstractions (like Transport) that can be switched at runtime.
    • I introduced a Transport abstraction as an implementation of an Adapter pattern in order to decouple from low level modules like fetch. So then when NodeJs fetch API reach an stable phase, it will be just a matter of creating a new Transport implementation with the new implementation. See NativeFechTransport code example ๐Ÿ‘‡
    • It's easy to add new Requests from the Petstore API by just implementing the class PetstoreRequest and add the new request to the list of supported requests at file PetstoreRequest.ts. See PurchaseOrderById code example ๐Ÿ‘‡
    • New responses can be added using Zod, and defining a new schema for the response and then add the new response model to the list of supported responses at the file Petstore.ts. See PurchaseOrder response model code example ๐Ÿ‘‡

Code examples

NativeFetchTransport

import { Transport } from "./Transport.js";
import { PetStoreRequests } from "./PetstoreRequest.js";

export class NativeFetchTransport implements Transport {
  constructor(private baseUrl: string) {}
  
  async execute(request: PetStoreRequests): Promise<Response> {
    return await fetch(`${this.baseUrl}${request.path()}`, {
      method: request.method(),
      headers: {
        Accept: 'application/json',
      },
    });
  }
}

PurchaseOrderById

import { HttpMethod, PetstoreRequest } from './PetstoreRequest.js';

export class PurchaseOrderById implements PetstoreRequest {
  constructor(readonly purchaseOrderId: number) {}
  path = (): string => `/store/order/${this.purchaseOrderId}`;
  method = (): HttpMethod => 'GET';
}

PurchaseOrder response model

import { z } from 'zod';

export const PurchaseOrder = z.object({
  id: z.number(),
  petId: z.number(),
  quantity: z.number(),
  shipDate: z.string(),
  status: z.literal("placed").or(z.literal("approved")).or(z.literal("delivered"))
});

export type PurchaseOrder = z.infer<typeof PurchaseOrder>;

Local development environment

I've added a small local environment based on Docker which includes 2 containers ๐Ÿ‘‡

  • The Petstore backend. It listens for requests at the port 8080.
  • The petstore client. It's just a container with no foreground process intended to run tests without having to install all the dependencies at local host.

To run it just

docker compose up -d

And then you should be able to see the Petstore Swagger UI at the URL ๐Ÿ‘‰ http://localhost:8080/. And then to access the container just run

docker compose run --rm petstore_client sh

petstore-client's People

Contributors

theunic avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.