Let's start by defining a few endpoints for our service. We'll be implementing the logic for these endpoints later; for now, we'll ensure the following routes are present in the API and return 200 OK responses.

User Routes

For the purpose of these routes, you can assume the user's handle looks like @userName. Don't worry about encoding/decoding the @ symbol in the URL. You can assume that the handle is unique for each user and is a valid URL path segment.

  • POST /api/user/:handle - Creates a new user
  • GET /api/user/:handle/posts - Returns all posts of a user by their handle, ordered by time

Post Routes

  • POST /api/posts - Creates a new post
  • GET /api/posts/:id - Returns a post by a text-based ID

Follower Routes

  • POST /api/follow/:handle - Follows a user by their handle

Feed Routes

  • GET /api/feed/:handle - Returns all posts of the people the user is following, ordered by time.

Workflow tip

The new namespace directive can be used to define a namespace prefix for a group of related functions. Add the following line to your scratch file to help keep your code organized and easier to read.

namespace exercises.ex3_microblog

Routes library basics

We'll be using the Routes library to define our endpoints.

There are two primary functions for defining a service path:

  • Route.noCapture - if your route does not capture any path parameters
  • Route.route - if your route extracts a path segment for use as a variable

The Routes library provides a few functions, such as ok.text or ok.json, that help build HTTP responses.

Combining the two—path definition and HTTP response construction—will create a complete endpoint:

api.createPost : '{Route} ()
api.createPost = do
  use Parser / s
  Route.noCapture POST (s "api" / s "posts")
  ok.text "ok"

The s "api" / s "posts" section of the route definition above sets up a series of path segments that match the /api/posts path.

Capturing a path segment would look like s "api" / s "posts" / Parser.text, where Parser.text represents the text of the path variable:

api.getPostById : '{Route} ()
api.getPostById = do
  use Parser / s
  postId =
    PostId
      (Route.route
        GET (s "api" / s "posts" / Parser.text))
  ok.text "ok"

We recommend defining one function per route, and then combining them all together with Route.<|> in a final call to Route.run. Route.run produces a function that takes an HttpRequest and returns an HttpResponse, which is the expected type for deploying an HTTP service.

Route.run : '{g, Route, Exception} ()
            -> HttpRequest
            ->{g, Exception} HttpResponse

📚 Instructions

Define the remaining endpoints by having them return a 200 OK response for each route. The response body does not matter for now; you can return ok.text "ok" for each route.

We've provided two route stubs to use as a reference for this part of the exercise:

  • api.getPostById - "GET /api/posts/:id"
  • api.createPost - "POST /api/posts"

The remaining routes are to implement are:

  • POST /api/user/:handle
  • GET /api/user/:handle/posts
  • POST /api/follow/:handle
  • GET /api/feed/:handle

Deploy all your routes as an HTTP service inside the provided ex3_microblog.deploy function. It should return the URI where the service is deployed.

cloud-start/main> edit ex3_microblog.deploy

When you've implemented all the routes, update your codebase and run your solution.

cloud-start/main> run submit.ex3_microblog.pt1
Solution

In addition to the provided api.getPostById and api.createPost functions, you should define the following routes:

namespace exercises.ex3_microblog

api.createUser : '{Route} ()
api.createUser = do
  userHandle = Route.route POST (s "api" / s "user" / Parser.text)
  ok.text "ok"

api.getUserPosts : '{Route} ()
api.getUserPosts = do
  userHandle = Route.route GET (s "api" / s "user" / Parser.text / s "posts")
  ok.text "ok"

api.followUser : '{Route} ()
api.followUser = do
  userHandle = Route.route POST (s "api" / s "follow" / Parser.text)
  ok.text "ok"

api.getFeed : '{Route} ()
api.getFeed = dos
  userHandle = Route.route GET (s "api" / s "feed" / Parser.text)
  ok.text "ok"

Combine all the routes with Route.run to create the HTTP service:

api : HttpRequest ->{Exception} HttpResponse
api  =
    use Route <|>
    Route.run
      (api.createPost
        <|> api.getPostById
        <|> api.getUserPosts
        <|> api.createUser
        <|> api.followUser
        <|> api.getFeed )

Deploy the service in the ex3_microblog.deploy function:

deploy : '{IO, Exception} URI
deploy = Cloud.main do
    hash = deployHttp exercises.env() api
    serviceName = ServiceName.create "microblog"
    ServiceName.assign serviceName hash