Implementing API Endpoints and Serialization
Build API endpoints, handle request and response bodies, and serialize data efficiently for client consumption.
Implementing API Endpoints and Serialization is a free Elixir & Phoenix: Scalable Backend Development lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Elixir & Phoenix: Scalable Backend Development learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
API Endpoints: The Basics
Welcome! In this lesson, we'll build API endpoints in Phoenix. An API endpoint is a specific URL that an API client can access to perform an action or retrieve data.
Think of it as a digital address for a specific resource, like /api/products or /api/users/123. Each endpoint usually corresponds to an action (e.g., get all products, create a new user).
Scaffolding an API Resource
Phoenix provides a handy generator to quickly set up a RESTful API resource. We'll use mix phx.gen.json to create a Product resource.
This command generates:
- A router entry
- A controller (e.g.,
ProductController) - An Ecto schema (e.g.,
Product) - A view (e.g.,
ProductView) for JSON serialization
mix phx.gen.json Products Product products name:string description:text price:decimalThe API Controller: Listing & Showing
After generating, let's look at a basic API controller. The index action handles GET /products to list all products. The show action handles GET /products/:id to display a single product.
Phoenix controllers use conn (the connection) to manage requests and responses. We fetch data and then render it as JSON.
defmodule MyAppWeb.ProductController do
use MyAppWeb, :controller
alias MyApp.Products
alias MyApp.Products.Product
action_fallback MyAppWeb.FallbackController
def index(conn, _params) do
products = Products.list_products()
render(conn, :index, products: products)
end
def show(conn, %{"id" => id}) do
product = Products.get_product!(id)
render(conn, :show, product: product)
end
endHandling Request Bodies: Create
When a client sends data to create a new resource (e.g., POST /products), that data is in the request body. Phoenix makes this data available in conn.body_params.
We typically pass this data to an Ecto Changeset for validation and then insert it into the database. If successful, we respond with the newly created resource; otherwise, we send an error.
Code: The `create` Action
Here's a simplified create action. Notice how we use conn.body_params and then render the result. (In a real app, create_product would involve Changesets and Ecto Repo calls).
Try to understand how the new product is rendered back to the client.
defmodule MyAppWeb.ProductController do
use MyAppWeb, :controller
alias MyApp.Products
alias MyApp.Products.Product
# ... other actions ...
def create(conn, %{"product" => product_params}) do
case Products.create_product(product_params) do
{:ok, product} ->
conn
|> put_status(:created)
|> render(:show, product: product)
{:error, %Ecto.Changeset{} = changeset} ->
conn
|> put_status(:unprocessable_entity)
|> render(MyAppWeb.ChangesetView, "error.json", changeset: changeset)
end
end
# Dummy function for runnable code
defp create_product(params) do
# In a real app, this would save to DB with Ecto.Changeset
# For demo, just return a dummy product
product = %Product{id: 1, name: params["name"], price: params["price"], description: params["description"]}
{:ok, product}
end
# For runnable context
defmodule Product do
defstruct [:id, :name, :price, :description]
end
endHandling Request Bodies: Update
The update action handles PUT or PATCH requests to modify an existing resource (e.g., PUT /products/:id). It's similar to create, but you first retrieve the existing resource.
The updated data is also found in conn.body_params. After validating and updating the resource, you typically respond with the updated resource or an error.
defmodule MyAppWeb.ProductController do
use MyAppWeb, :controller
alias MyApp.Products
alias MyApp.Products.Product
# ... other actions ...
def update(conn, %{"id" => id, "product" => product_params}) do
product = Products.get_product!(id)
case Products.update_product(product, product_params) do
{:ok, product} ->
render(conn, :show, product: product)
{:error, %Ecto.Changeset{} = changeset} ->
conn
|> put_status(:unprocessable_entity)
|> render(MyAppWeb.ChangesetView, "error.json", changeset: changeset)
end
end
# Dummy functions for runnable context
defp get_product!(id), do: %Product{id: String.to_integer(id), name: "Old Name", price: "10.0", description: "Old Description"}
defp update_product(product, params) do
updated_product = %{product | name: Map.get(params, "name", product.name), price: Map.get(params, "price", product.price)}
{:ok, updated_product}
end
defmodule Product do
defstruct [:id, :name, :price, :description]
end
endWhat is Serialization?
Serialization is the process of converting Elixir data structures (like structs or maps) into a format that can be easily transmitted over a network or stored. For APIs, this usually means converting Elixir data into JSON (JavaScript Object Notation).
Why is it important? It allows different systems (your Elixir backend and a mobile app or web frontend) to understand and exchange data consistently.
Basic JSON Serialization with `render`
Phoenix uses views to handle serialization. When you call render(conn, :show, product: product) in a controller, Phoenix looks for a corresponding view module (e.g., ProductView) and a function (e.g., render("show.json", %{product: product})).
This function then defines how the Elixir product struct should be transformed into a JSON map.
defmodule MyAppWeb.ProductView do
use MyAppWeb, :view
def render("index.json", %{products: products}) do
%{data: render_many(products, __MODULE__, :product)}
end
def render("show.json", %{product: product}) do
%{data: render_one(product, __MODULE__, :product)}
end
def product(product) do
%{id: product.id,
name: product.name,
description: product.description,
price: product.price}
end
endCustomizing JSON Output
The product/1 function within ProductView is where you define the exact structure of your JSON response. You can:
- Select specific fields to include or exclude.
- Rename fields for client-friendliness.
- Nest related data (e.g., include a product's category).
This gives you fine-grained control over what data your API exposes and how it's structured.
Quick Check: Request Bodies
Consider a Phoenix API endpoint designed to create a new user. A client sends a POST request to /api/users with a JSON body:
{"user": {"name": "Alice", "email": "alice@example.com"}}Which part of the conn struct holds the data {"name": "Alice", "email": "alice@example.com"}?
Recap: Endpoints & Serialization
Great job! You've learned how to build and understand Phoenix API endpoints:
- API Endpoints: Specific URLs for resource actions.
- Request Bodies: Data sent by clients (e.g., for
POST/PUT) is inconn.body_params. - Serialization: Converting Elixir data to JSON for API responses, primarily handled by Phoenix views.
- Customization: Views allow you to control the exact JSON structure.
These concepts are fundamental to building robust and client-friendly RESTful APIs with Phoenix!
Frequently asked questions
Is the “Implementing API Endpoints and Serialization” lesson free?
Yes — the full text of “Implementing API Endpoints and Serialization” is free to read here on the web, and the Elixir & Phoenix: Scalable Backend Development course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Elixir & Phoenix: Scalable Backend Development course, upgrade to CoddyKit PRO.
What will I learn in “Implementing API Endpoints and Serialization”?
Build API endpoints, handle request and response bodies, and serialize data efficiently for client consumption. You practise Elixir & Phoenix: Scalable Backend Development with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Elixir & Phoenix: Scalable Backend Development?
No prior experience is required. Elixir & Phoenix: Scalable Backend Development on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Implementing API Endpoints and Serialization” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Elixir & Phoenix: Scalable Backend Development lesson?
Yes. Every Elixir & Phoenix: Scalable Backend Development lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- API Design Principles and Best Practices
- Implementing API Endpoints and Serialization
- Authentication and Authorization Strategies
- Pagination, Filtering & API Versioning