servant

repository·master·Indexed 24 days ago

https://github.com/haskell-servant/servant

A type-level web DSL for Haskell that allows developers to define web APIs at the type level for high type safety and consistency. The ecosystem includes servant-client for automatic Haskell client generation, servant-client-ghcjs for browser-based requests, servant-docs for automatic Markdown documentation, servant-foreign for generating clients in other languages, and servant-quickcheck for property-based API testing.

Tokens
10.7K
Snippets
34
Records
62
Agent score
79%

What's inside servant

  1. Overview of servant's type-safe web DSL

    master

    Servant is a set of Haskell libraries that allows you to define a web API as a Haskell type. This single source of truth enables several automated tasks:

    • Server Implementation: Servant checks that your server-side request handlers faithfully implement the defined API type.
    • Client Derivation: Automatically derive Haskell functions (or code in other languages) to interact with the API.
    • Documentation Generation: Automatically generate Swagger descriptions or other API documentation from the type definition.
  2. Implement an HTTP server with servant-server

    master
    The servant-server library allows you to implement an HTTP server by providing handlers for each endpoint defined in a servant API specification. It automates the boilerplate required to map incoming HTTP requests to your Haskell functions based on the type-level API definition.
  3. Generate Swagger 2.0 specifications for Servant APIs

    master

    The servant-swagger package allows you to generate Swagger 2.0 conforming JSON specifications directly from your servant API definitions. This specification can be used to:

    • Display interactive documentation via Swagger UI.
    • Generate client and server code in multiple languages using Swagger Codegen.
    • Integrate with various other Swagger-compatible tools.

    For detailed implementation details, refer to the Haddock documentation or examine the examples in the example/ directory of the repository.

  4. Generate clients for servant servers in other languages using servant-foreign

    master
    servant-foreign provides types and helper functions to facilitate the generation of API clients for servant servers in arbitrary programming languages. It allows you to leverage your Haskell type-level API definitions to produce client code for non-Haskell environments.
  5. Implement a custom monad for servant applications

    master

    Instead of using IO directly in your servant handlers, you can use a custom monad (like the one demonstrated in the diener repository) to provide functionality such as logging and a Reader monad for managing database connections.

    https://github.com/themoritz/diener
  6. Handle HTTP Headers and Request Metadata

    master

    Servant provides mechanisms to access various parts of the incoming HTTP request:

    • Headers: Access specific HTTP headers (e.g., foo).
    • Remote Host: Access the IP address or hostname of the client.
    • HTTP Version: Access the version of the HTTP protocol used in the request.
    • Security Context: Check if the request is secure (e.g., via HTTPS).
  7. Define Request and Response Bodies

    master

    Servant handles the serialization and deserialization of data using content types (most commonly application/json).

    • Request Body: Data sent in the body of a request (typically for POST or PUT).
    • Response Body: Data returned by the server. The response includes a status code (e.g., 200 OK, 204 No Content) and can include custom headers.
  8. Core design principles of servant

    master

    The servant framework is built around four guiding principles that shape how you design and interact with your web services:

    • Concision: Avoid repetition. You should declare serialization/deserialization logic once per type, and use the API description to automatically generate documentation, client libraries, and shared query parameter logic across multiple handlers.
    • Flexibility: The framework is non-opinionated. You can use any templating library or form-handling method you prefer without fighting the framework.
    • Separation of Concerns: Keep HTTP logic separate from business logic. Handlers should return standard Haskell datatypes (the 'resource'), while servant uses the API description to handle the 'presentation' (e.g., managing Content-Types).
    • Type Safety: Use the Haskell compiler to verify that your implementation meets your API specification and to ensure that links within your application are valid.
  9. Create cross-platform clients using servant-client-core

    master

    To write code that works for both servant-client (server-side/desktop) and servant-client-ghcjs (browser), use the servant-client-core package.

    Instead of generating functions that return ClientM, you define a record type parameterized by a monad m. You then use clientIn to populate this record. This requires the monad m to satisfy the RunClient constraint.

    Pattern:

    1. Define a record APIClient m containing the API functions.
    2. Use clientIn with Proxy @API and Proxy @m to implement the record.
    3. At the call site, provide the specific runner (e.g., runClientM for GHCJS or runClientM with a manager for servant-client).
    import Servant.Client.Core
    
    data APIClient m = APIClient
      { position  :: Int -> Int -> m Position
      , hello     :: Maybe String -> m HelloMessage
      , marketing :: ClientInfo -> m Email
      }
    
    apiClient
        :: forall m
         . RunClient m
        => APIClient m
    apiClient = APIClient { .. }
      where
        position
          :<|> hello
          :<|> marketing = Proxy @API `clientIn` Proxy @m
  10. Capture path segments in Servant APIs

    master

    Servant allows capturing parts of a URL path as variables. These captures are passed to the handler as arguments.

    • Standard Capture: Captures a segment of the path (e.g., :bar).
    • Capture All: Captures the remainder of the path (e.g., :foo).
    • Lenient Capture: Captures a segment but allows for more flexible parsing (e.g., :foo).
  11. Reifying API descriptions as types

    master

    The fundamental mechanism of servant is the reification of the API description as a type. Instead of writing imperative routing logic, you define your API structure at the type level. Once this description is reified, servant can automatically derive various components such as:

    • Server implementations
    • Client libraries
    • Documentation
    • Content-Type handling (presentation layer)
  12. Use Query Parameters and Flags in Servant

    master

    You can define parameters that appear in the query string of a GET request.

    • Standard Query Parameter: A key-value pair (e.g., ?foo=1).
    • Flag: A parameter that does not expect a value; its presence alone is sufficient (e.g., ?foo).
    • List Parameter: A parameter that accepts multiple values. To pass a list, use the name with brackets, such as foo[] (e.g., ?foo[]=1&foo[]=2).
    • Enum/Value Constraints: Parameters can be restricted to specific allowed values.