{"id":9050,"date":"2022-07-27T17:17:53","date_gmt":"2022-07-28T00:17:53","guid":{"rendered":"https:\/\/coderpad.io\/?p=9050"},"modified":"2023-06-07T14:06:11","modified_gmt":"2023-06-07T21:06:11","slug":"everything-you-need-to-know-to-map-sql-tables-to-rest-endpoints-in-python","status":"publish","type":"post","link":"https:\/\/coderpad.io\/blog\/development\/everything-you-need-to-know-to-map-sql-tables-to-rest-endpoints-in-python\/","title":{"rendered":"Everything You Need to Know to Map SQL Tables to REST Endpoints in Python"},"content":{"rendered":"\n<p>The backbone of back-end programming is the act of sending, receiving, and manipulating data. The two most common ways of interacting with backend data are SQL (&#8220;Structured Query Language&#8221;) and REST (&#8220;REpresentational State Transfer&#8221;) APIs. SQL is the method that many servers use to store their data, while REST is a method of transferring that data to and from the client. Of course, there are many ways to store and send data, but these are the most common and are widely supported.<\/p>\n\n\n\n<p>To start working with SQL and REST, the first thing you need to understand is how they actually integrate together.<\/p>\n\n\n\n<p>To illustrate this, let\u2019s imagine you\u2019re an event business; there\u2019s lots of data that you need to store and access. For example, you have events, bills for those events, and people attending those events and paying those bills. This blog explains the different kinds of relationships between pieces of data in SQL and how to use the four REST verbs to create a REST API to give users the ability to interact with the data stored in SQL.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"#intro-to-sql-relationships\">Introduction to SQL relationships<\/a>\n<ul class=\"wp-block-list\">\n<li><a href=\"#one-to-one\">One to one<\/a><\/li>\n\n\n\n<li><a href=\"#one-to-many\">One to many<\/a><\/li>\n\n\n\n<li><a href=\"#many-to-many\">Many to many<\/a><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><a href=\"#intro-to-rest-api-design\">Introduction to REST API design<\/a>\n<ul class=\"wp-block-list\">\n<li><a href=\"#get-and-post\">Grab and create data using GET and POST<\/a><\/li>\n\n\n\n<li><a href=\"#patch\">PATCH your data to update values<\/a><\/li>\n\n\n\n<li><a href=\"#delete\">How to DELETE your data<\/a><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><a href=\"#present-relationships-rest-api\">How to represent relationships in a REST API<\/a>\n<ul class=\"wp-block-list\">\n<li><a href=\"#many-to-many-dynamic-routes\">Many-to-many relationships require dynamic routes<\/a><\/li>\n\n\n\n<li><a href=\"#one-to-one-sub-endpoints\">One to One relationships don\u2019t need sub-endpoints<\/a><\/li>\n\n\n\n<li><a href=\"#one-to-many-combined-approaches\">One to Many relationships combine One to One and Many to Many approaches<\/a><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><a href=\"#all-api-endpoints\">All API Endpoints<\/a><\/li>\n<\/ul>\n\n\n<aside class=\"\n    cta-banner\n     cta-banner--bg-green      cta-banner--has-media \"\ndata-block-name=\"cta-banner\">\n    <div class=\"inner\">\n        <div class=\"content\">\n                            <h2 class=\"headline\">Ready to start interviewing?<\/h2>\n            \n                            <div class=\"cta-buttons\">\n                                    <a href=\"\/sales\/\" class=\"button  js-cta--get-a-demo\"  data-ga-category=\"CTA\" data-ga-label=\"Ready to start interviewing?|Get a demo\">Get a demo<\/a>\n                                <\/div>\n                    <\/div>\n                    <div class=\"media\">\n                <img loading=\"lazy\" decoding=\"async\" width=\"238\" height=\"146\" src=\"https:\/\/coderpad.io\/wp-content\/uploads\/2022\/08\/illustration-of-two-people-chatting-in-app-windows.png\" class=\"attachment-large size-large\" alt=\"\" \/>\n            <\/div>\n            <\/div>\n<\/aside>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"intro-to-sql-relationships\">Introduction to SQL relationships<\/h2>\n\n\n\n<p>SQL is just a fancy way of saying it\u2019s a database that stores data in tables (rows and columns, not things to eat off of!).&nbsp;<\/p>\n\n\n\n<p>One of the many common themes in programming that SQL emphasizes is avoiding duplication of data. After all, who wants to have to change a field in multiple places when only one piece of data changed? Let\u2019s min\/max the effort spent.<\/p>\n\n\n\n<p>In order to accomplish this, SQL needs a way to say that certain pieces of data are \u2018related\u2019 to other pieces of data. Putting all the data in one table would be rather unwieldy! However, there still needs to be a way to associate different pieces of data \u2013 like how you need to know the syntax for a programming language, but it\u2019s useless if you don\u2019t know how to compile or interpret the language.<\/p>\n\n\n\n<p>Hence, there are three types of \u201crelationships\u201d in SQL: one to one, one to many, and many to many.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"one-to-one\">One to one<\/h3>\n\n\n\n<p>As the name implies, one to one&nbsp; means that there is only one of a certain thing related to another thing. For example, one event has only one bill (in a perfect world), and a bill is only for one event.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/d2h1bfu6zrdxog.cloudfront.net\/wp-content\/uploads\/2022\/07\/img_62e19a22a9bea.png\" alt=\"\"\/><figcaption class=\"wp-element-caption\">One bill is related to one event<\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/d2h1bfu6zrdxog.cloudfront.net\/wp-content\/uploads\/2022\/07\/img_62e19a23815ff.png\" alt=\"\"\/><figcaption class=\"wp-element-caption\">Bills have a foreign key \u2018relationship\u2019 to one event<\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"one-to-many\"><strong>One to many<\/strong>&nbsp;<\/h3>\n\n\n\n<p>Here, one piece of data is related to multiple pieces of data of the same type. For example, one person can have many bills, but those bills are only meant to be paid by one person. Or, one cat may have nine lives, but each of those lives belongs to only one cat.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/d2h1bfu6zrdxog.cloudfront.net\/wp-content\/uploads\/2022\/07\/img_62e19a23cd02f.png\" alt=\"\"\/><figcaption class=\"wp-element-caption\">One person has many bills<\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/d2h1bfu6zrdxog.cloudfront.net\/wp-content\/uploads\/2022\/07\/img_62e19a2476cb8.png\" alt=\"\"\/><figcaption class=\"wp-element-caption\">Each bill in the bills table is related to one person<\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"many-to-many\"><strong>Many to many<\/strong>&nbsp;<\/h3>\n\n\n\n<p>Here, events can have multiple people attend them, and people can attend multiple events. For another example, a code repository can have multiple contributors, and each contributor can have multiple code repositories. Imagine if you could only have one!<\/p>\n\n\n\n<p>The next question, though, is <strong>how do we store a many to many relationship without repeating data<\/strong>? The answer is that you can\u2019t completely. But the next best thing is only having minimal data duplication. At first, you may think to make a list of IDs of related data, but lists are difficult to edit in SQL. So, the idiomatic solution for SQL is \u2018junction\u2019 tables!<\/p>\n\n\n\n<p>A junction table typically has a name that is the names of two tables that have a many-to-many relationship squished together, and consists of foreign keys (meaning they reference the primary keys in other tables). So, for <code>people<\/code> and <code>events<\/code> the junction table would be <code>people_events<\/code>.<\/p>\n\n\n\n<p><br>All of this means that every row in the table represents a person id and an event id, and multiple people can be signed up for one event and multiple events can be attended by one person. This leaves some duplication of data, but it is minimal. The table would look like:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/d2h1bfu6zrdxog.cloudfront.net\/wp-content\/uploads\/2022\/07\/img_62e19a24a1726.png\" alt=\"\"\/><figcaption class=\"wp-element-caption\">One event can have many people attending, and one person can attend multiple events<\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/d2h1bfu6zrdxog.cloudfront.net\/wp-content\/uploads\/2022\/07\/img_62e19a256e108.png\" alt=\"\"\/><figcaption class=\"wp-element-caption\">A junction table has a row relating to one person and one event, showing the relationship<\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\"><span class=\"hljs-comment\">-- create tables (people, events, bills, people_events)<\/span>\n\n<span class=\"hljs-keyword\">create<\/span> <span class=\"hljs-keyword\">table<\/span> people\n(\n\t<span class=\"hljs-keyword\">id<\/span>            <span class=\"hljs-built_in\">SERIAL<\/span>,\n\t<span class=\"hljs-keyword\">name<\/span>          <span class=\"hljs-built_in\">varchar<\/span>(<span class=\"hljs-number\">255<\/span>),\n\n\tprimary <span class=\"hljs-keyword\">key<\/span>(<span class=\"hljs-keyword\">id<\/span>)\n);\n\n<span class=\"hljs-keyword\">create<\/span> <span class=\"hljs-keyword\">table<\/span> <span class=\"hljs-keyword\">events<\/span>\n(\n\t<span class=\"hljs-keyword\">id<\/span>            <span class=\"hljs-built_in\">SERIAL<\/span>,\n  \t<span class=\"hljs-built_in\">date<\/span>\t\t  <span class=\"hljs-built_in\">DATE<\/span> <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span> <span class=\"hljs-keyword\">DEFAULT<\/span> <span class=\"hljs-keyword\">CURRENT_DATE<\/span>,\n    <span class=\"hljs-keyword\">type<\/span>\t\t  <span class=\"hljs-built_in\">varchar<\/span>(<span class=\"hljs-number\">255<\/span>),\n\n\tprimary <span class=\"hljs-keyword\">key<\/span>(<span class=\"hljs-keyword\">id<\/span>)\n);\n\n<span class=\"hljs-keyword\">create<\/span> <span class=\"hljs-keyword\">table<\/span> bills\n(\n\t<span class=\"hljs-keyword\">id<\/span>            <span class=\"hljs-built_in\">SERIAL<\/span>,\n\t<span class=\"hljs-keyword\">cost<\/span>          <span class=\"hljs-built_in\">int<\/span>,\n  \tpaid\t\t  <span class=\"hljs-built_in\">varchar<\/span>(<span class=\"hljs-number\">255<\/span>) <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-string\">'not paid'<\/span>,\n  \tdue\t\t\t  <span class=\"hljs-built_in\">DATE<\/span> <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span> <span class=\"hljs-keyword\">DEFAULT<\/span> <span class=\"hljs-keyword\">CURRENT_DATE<\/span>,\n  \tpayer_id\t  <span class=\"hljs-built_in\">int<\/span>,\n  \tevent_id\t  <span class=\"hljs-built_in\">int<\/span>,\n\n\tprimary <span class=\"hljs-keyword\">key<\/span>(<span class=\"hljs-keyword\">id<\/span>),\n  \t<span class=\"hljs-keyword\">foreign<\/span> <span class=\"hljs-keyword\">key<\/span> (payer_id) <span class=\"hljs-keyword\">references<\/span> people(<span class=\"hljs-keyword\">id<\/span>),\n    <span class=\"hljs-keyword\">foreign<\/span> <span class=\"hljs-keyword\">key<\/span> (event_id) <span class=\"hljs-keyword\">references<\/span> <span class=\"hljs-keyword\">events<\/span>(<span class=\"hljs-keyword\">id<\/span>)\n);\n\n<span class=\"hljs-keyword\">create<\/span> <span class=\"hljs-keyword\">table<\/span> people_events\n(\n\tpeople_id\t  <span class=\"hljs-built_in\">int<\/span>,\n  \tevent_id      <span class=\"hljs-built_in\">int<\/span>,\n\n  \t<span class=\"hljs-keyword\">foreign<\/span> <span class=\"hljs-keyword\">key<\/span> (people_id) <span class=\"hljs-keyword\">references<\/span> people(<span class=\"hljs-keyword\">id<\/span>),\n    <span class=\"hljs-keyword\">foreign<\/span> <span class=\"hljs-keyword\">key<\/span> (event_id) <span class=\"hljs-keyword\">references<\/span> <span class=\"hljs-keyword\">events<\/span>(<span class=\"hljs-keyword\">id<\/span>)\n);\n\n<span class=\"hljs-comment\">-- populate with sample data<\/span>\n\n<span class=\"hljs-keyword\">insert<\/span> <span class=\"hljs-keyword\">into<\/span> people (<span class=\"hljs-keyword\">name<\/span>) <span class=\"hljs-keyword\">values<\/span> (<span class=\"hljs-string\">'PersonA'<\/span>), (<span class=\"hljs-string\">'PersonB'<\/span>), (<span class=\"hljs-string\">'PersonC'<\/span>);\n\n<span class=\"hljs-keyword\">insert<\/span> <span class=\"hljs-keyword\">into<\/span> <span class=\"hljs-keyword\">events<\/span> (<span class=\"hljs-built_in\">date<\/span>, <span class=\"hljs-keyword\">type<\/span>)\n<span class=\"hljs-keyword\">values<\/span> (<span class=\"hljs-string\">'2023-02-20'<\/span>, <span class=\"hljs-string\">'concert'<\/span>), (<span class=\"hljs-string\">'2022-06-30'<\/span>, <span class=\"hljs-string\">'conference'<\/span>), (<span class=\"hljs-string\">'2025-12-20'<\/span>, <span class=\"hljs-string\">'holiday party'<\/span>);\n\n<span class=\"hljs-keyword\">insert<\/span> <span class=\"hljs-keyword\">into<\/span> bills (<span class=\"hljs-keyword\">cost<\/span>, paid, due, payer_id, event_id) <span class=\"hljs-keyword\">values<\/span> (<span class=\"hljs-number\">2000<\/span>, <span class=\"hljs-string\">'paid'<\/span>, <span class=\"hljs-string\">'2022-02-20'<\/span>, <span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">1<\/span>), (<span class=\"hljs-number\">10000<\/span>, <span class=\"hljs-string\">'not paid'<\/span>, <span class=\"hljs-string\">'2021-06-01'<\/span>, <span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">2<\/span>), (<span class=\"hljs-number\">5000<\/span>, <span class=\"hljs-string\">'paid'<\/span>, <span class=\"hljs-string\">'2025-01-20'<\/span>, <span class=\"hljs-number\">2<\/span>, <span class=\"hljs-number\">3<\/span>);\n\n<span class=\"hljs-keyword\">insert<\/span> <span class=\"hljs-keyword\">into<\/span> people_events (people_id, event_id) <span class=\"hljs-keyword\">values<\/span> (<span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">1<\/span>), (<span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">2<\/span>), (<span class=\"hljs-number\">2<\/span>, <span class=\"hljs-number\">3<\/span>), (<span class=\"hljs-number\">2<\/span>, <span class=\"hljs-number\">1<\/span>);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now that we have our tables (people, bills, events, and people_events) the next step is making them usable without having to actually connect to a database. After all, we want to control what someone can change in the database, give ourselves a space to write business logic, and give users an easier interface to use our database with. The ideal way to do this is to map our database\u2019s functionality to API (Application Programming Interface) endpoints.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/d2h1bfu6zrdxog.cloudfront.net\/wp-content\/uploads\/2022\/07\/img_62e19a258b19b.png\" alt=\"\"\/><figcaption class=\"wp-element-caption\">Our four tables with example data and their relationships<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/d2h1bfu6zrdxog.cloudfront.net\/wp-content\/uploads\/2022\/07\/img_62e19a2688e19.png\" alt=\"\"\/><figcaption class=\"wp-element-caption\">Our four tables and their relationships<\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"intro-to-rest-api-design\">Introduction to REST API design<\/h2>\n\n\n\n<p>One type of API is REST, which stands for Representational State Transfer. REST is a standard for how to change the state of data. REST defines an \u2018endpoint\u2019 as an url (just like the one you navigated to in order to read this blog) and a \u2018verb\u2019, which is what changes the data is experiencing.<\/p>\n\n\n\n<p>There are four main REST verbs:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>GET<\/strong>: Literally means we \u2018get\u2019 the requested resource (data). You performed a \u201cGET\u201d just to get here.<\/li>\n\n\n\n<li><strong>POST<\/strong>: This is how new data is created. We \u2018post\u2019 data to the API (like how we \u2018post\u2019 mail or comments).<\/li>\n\n\n\n<li><strong>PATCH<\/strong>: Also sometimes called \u201cPUT\u201d, this is how data that exists is edited. It\u2019d be a pain if we had to delete data and re-\u2019post\u2019 the data every time something changes, so we use \u2018patch\u2019 instead. To understand how annoying it could be without \u2018patch\u2019, imagine if the day of the week was a value in a table, and ergo you had to DELETE and POST it every day.<\/li>\n\n\n\n<li><strong>DELETE<\/strong>: This is how existing data is removed.<\/li>\n<\/ol>\n\n\n\n<p>With these four verbs we can provide our users the full CRUD (create, read, update, delete) functionality via the API to interact with our database tables.<\/p>\n\n\n\n<p>But wait. If we give an API the URL <code>\/people<\/code> and the verb POST (or PATCH)&#8230; how does the API know what data about the person to put in the database? Does it randomly generate a row in the table? That wouldn\u2019t make much sense. Instead, the information about the person or other resource to create is given in the \u2018body\u2019 of the request. Typically this data is given in the form of JSON, and looks a bit like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json shcb-wrap-lines\">{\n   <span class=\"hljs-attr\">\"name\"<\/span>:<span class=\"hljs-string\">\"New Person\"<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<p>POST and PATCH are the two verbs that expect a \u2018body\u2019 in their request. Because of this you should also pass in a header (\u201cContent-type\u201d) telling the API what type of data you\u2019re passing in (in this case, since we\u2019re returning JSON, it would be \u201cContent-type: application\/json\u201d). The content type header is optional, and many APIs do not require it, but it is good practice.<\/p>\n\n\n\n<p>An example of some REST headers would be:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTTP\" data-shcb-language-slug=\"http\"><span><code class=\"hljs language-http shcb-wrap-lines\">HTTP\/1.1 <span class=\"hljs-number\">200<\/span> OK\n<span class=\"hljs-attribute\">Date<\/span>: Fri, 08 Jul 2022 22:54:27 GMT\n<span class=\"hljs-attribute\">Content-type<\/span>: application\/json\n<span class=\"hljs-attribute\">Connection<\/span>: keep-alive\n<span class=\"hljs-attribute\">Vary<\/span>: Accept-Encoding, Origin\n<span class=\"hljs-attribute\">Content-Encoding<\/span>: gzip<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTTP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">http<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"get-and-post\">Grab and create data using GET and POST<\/h3>\n\n\n\n<p>When we\u2019re thinking about how to design the API it should be noted that APIs are intended to be how programs talk to each other (meaning they\u2019re intended for code such as UIs to call them, not for people to call them manually). This means that the priority when designing an API is <em>not<\/em> being easy for humans to manually use, but, instead, being easy for programs to interact with. Of course, when both can be achieved that is preferable.<\/p>\n\n\n\n<p>The most straightforward way to start designing an API is to allow endpoints for each REST verb for each table (so the user has full CRUD access to each table).<\/p>\n\n\n\n<p>In our case that means that for the table \u2018people\u2019 we start by creating the endpoint <code>\/people<\/code> with the verb GET (so the user can \u201cGET\u201d all the people). This is called a \u2018collection\u2019 endpoint because it returns one or more resources (in this case, one resource is one person).<\/p>\n\n\n\n<p>And in order to create new people (in the database, not real life!), the <code>\/people<\/code> URL with the verb POST endpoint comes next.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote\">\n<p>Please note that, for this blog\u2019s purposes, we\u2019ll be using a Python based tool called Flask to create our API and a Python package called SQLAlchemy to access our database.<\/p>\n<\/blockquote>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> sqlalchemy\n<span class=\"hljs-keyword\">from<\/span> sqlalchemy.orm <span class=\"hljs-keyword\">import<\/span> sessionmaker\n<span class=\"hljs-comment\"># from flask import Flask, Response, request<\/span>\n<span class=\"hljs-keyword\">from<\/span> typing <span class=\"hljs-keyword\">import<\/span> Dict\n\n<span class=\"hljs-comment\"># app = Flask(__name__)<\/span>\n\n<span class=\"hljs-comment\"># Connect to the DB and reflect metadata.<\/span>\nengine = sqlalchemy.create_engine(<span class=\"hljs-string\">\"postgresql:\/\/coderpad:@\/coderpad?host=\/tmp\/postgresql\/socket\"<\/span>)\nconnection = engine.connect()\nSession = sessionmaker(bind=engine)\nsession = Session()\nmetadata = sqlalchemy.MetaData()\nmetadata.reflect(bind=engine)\n\n\n<span class=\"hljs-comment\"># @app.route(\"\/people\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_all<\/span><span class=\"hljs-params\">()<\/span> -&gt; Dict:<\/span>\n    people_table = metadata.tables&#91;<span class=\"hljs-string\">\"people\"<\/span>]\n    result = connection.execute(people_table.select()).fetchall()\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: &#91;\n            dict(person) <span class=\"hljs-keyword\">for<\/span> person <span class=\"hljs-keyword\">in<\/span> result\n        ]\n    }\n\n\n<span class=\"hljs-comment\"># @app.route(\"\/people\", methods=&#91;\"POST\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">post<\/span><span class=\"hljs-params\">()<\/span> -&gt; Response:<\/span>\n    content_type = request.headers.get(<span class=\"hljs-string\">\"Content-Type\"<\/span>)\n    <span class=\"hljs-keyword\">if<\/span> (content_type != <span class=\"hljs-string\">\"application\/json\"<\/span>):\n        <span class=\"hljs-keyword\">return<\/span> {\n            <span class=\"hljs-string\">\"result\"<\/span>: <span class=\"hljs-string\">\"Failure\"<\/span>,\n            <span class=\"hljs-string\">\"reason\"<\/span>: <span class=\"hljs-string\">\"Content-Type not supported!\"<\/span>,\n        }\n\n    people_table = metadata.tables&#91;<span class=\"hljs-string\">\"people\"<\/span>]\n    <span class=\"hljs-comment\"># There should be validation that the POST body is valid here!<\/span>\n    connection.execute(\n        sqlalchemy.insert(people_table).values(\n            **request.json,\n        )\n    )\n    <span class=\"hljs-keyword\">return<\/span> Response(status=<span class=\"hljs-number\">201<\/span>)  <span class=\"hljs-comment\"># Created<\/span>\n\n\n<span class=\"hljs-comment\"># if __name__ == \"__main__\":<\/span>\n<span class=\"hljs-comment\">#     app.run()<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<div\n\tclass=\"sandbox-embed responsive-embed  sandbox-embed--full-width\"\n\tstyle=\"padding-top: 125%\"\ndata-block-name=\"coderpad-sandbox-embed\">\n\t<iframe src=\"https:\/\/embed.coderpad.io\/sandbox?question_id=222956&#038;use_question_button\" width=\"640\" height=\"800\" loading=\"lazy\" aria-label=\"Try out the CoderPad sandbox\"><\/iframe>\n<\/div>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"patch\">PATCH your data to update values<\/h3>\n\n\n\n<p>PATCH is the next REST verb we&#8217;ll touch on. PATCH gives users the ability to change data about resources. While we <em>could<\/em>use PATCH for the &#8216;\/people&#8217; endpoint &#8211; which would enable users to make &#8220;bulk&#8221; changes &#8211; let&#8217;s focus on the use-case of modifying<strong>one<\/strong>&#8220;person&#8221; at a time.<\/p>\n\n\n\n<p>When considering if you want bulk endpoints, the question is \u201cdoes the user want to be able to change multiple rows in your table at a time?\u201d. If yes, then you want bulk endpoints; if not, then this endpoint is extraneous.<\/p>\n\n\n\n<p>To change one resource, then, we need a way to tell the API what row in the table to change. There are multiple ways to do that (such as query parameters), but the standard way of doing this is by putting the ID (or other unique identifier) of the resource in the url.<\/p>\n\n\n\n<p>To update the first person created in the table, the endpoint would be <code>\/people\/1<\/code> with the verb PATCH. This endpoint would update the person with the ID 1 using whatever information is passed in the \u2018body\u2019 of the request.&nbsp;<\/p>\n\n\n\n<p>When documenting APIs this is usually shown by specifying the unique identifier using <code>:attributeName<\/code> or, in the case of Flask <code>&lt;datatype: attributeName&gt;<\/code>. In the Flask way of doing things, our PATCH endpoint would be <code>\/people\/&lt;int: id&gt;<\/code> with the verb PATCH.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> sqlalchemy\n<span class=\"hljs-keyword\">from<\/span> sqlalchemy.orm <span class=\"hljs-keyword\">import<\/span> sessionmaker\n<span class=\"hljs-keyword\">from<\/span> flask <span class=\"hljs-keyword\">import<\/span> Flask, Response, request\n\napp = Flask(__name__)\n\n<span class=\"hljs-comment\"># Connect to the DB and reflect metadata.<\/span>\nengine = sqlalchemy.create_engine(<span class=\"hljs-string\">\"postgresql:\/\/coderpad:@\/coderpad?host=\/tmp\/postgresql\/socket\"<\/span>)\nconnection = engine.connect()\nSession = sessionmaker(bind=engine)\nsession = Session()\nmetadata = sqlalchemy.MetaData()\nmetadata.reflect(bind=engine)\n\n<span class=\"hljs-meta\">@app.route(\"\/people\/&lt;int:id&gt;\", methods=&#91;\"PATCH\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">patch<\/span><span class=\"hljs-params\">(id: int)<\/span> -&gt; Response:<\/span>\n    content_type = request.headers.get(<span class=\"hljs-string\">\"Content-Type\"<\/span>)\n    <span class=\"hljs-keyword\">if<\/span> (content_type != <span class=\"hljs-string\">\"application\/json\"<\/span>):\n        <span class=\"hljs-keyword\">return<\/span> {\n            <span class=\"hljs-string\">\"result\"<\/span>: <span class=\"hljs-string\">\"Failure\"<\/span>,\n            <span class=\"hljs-string\">\"reason\"<\/span>: <span class=\"hljs-string\">\"Content-Type not supported!\"<\/span>,\n        }\n\n    people_table = metadata.tables&#91;<span class=\"hljs-string\">\"people\"<\/span>]\n    <span class=\"hljs-comment\"># There should be validation that the PATCH body is valid here!<\/span>\n    connection.execute(\n        sqlalchemy.update(people_table).where(\n            people_table.c.id == id\n        ).values(\n            **request.json,\n        )\n    )\n    <span class=\"hljs-keyword\">return<\/span> Response(status=<span class=\"hljs-number\">204<\/span>)  <span class=\"hljs-comment\"># No Content<\/span>\n\n\n<span class=\"hljs-keyword\">if<\/span> __name__ == <span class=\"hljs-string\">\"__main__\"<\/span>:\n    app.run()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\" id=\"delete\">How to DELETE your data<\/h3>\n\n\n\n<p>And when we get to the DELETE verb (to add the ability to delete people) we have the option to make a bulk endpoint. But, for brevity\u2019s sake, we\u2019ll focus on deleting one person at a time, using the endpoint <code>\/people\/&lt;int: id&gt;<\/code> with the verb DELETE.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> sqlalchemy\n<span class=\"hljs-keyword\">from<\/span> sqlalchemy.orm <span class=\"hljs-keyword\">import<\/span> sessionmaker\n<span class=\"hljs-keyword\">from<\/span> flask <span class=\"hljs-keyword\">import<\/span> Flask, Response\n\napp = Flask(__name__)\n\n<span class=\"hljs-comment\"># Connect to the DB and reflect metadata.<\/span>\nengine = sqlalchemy.create_engine(<span class=\"hljs-string\">\"postgresql:\/\/coderpad:@\/coderpad?host=\/tmp\/postgresql\/socket\"<\/span>)\nconnection = engine.connect()\nSession = sessionmaker(bind=engine)\nsession = Session()\nmetadata = sqlalchemy.MetaData()\nmetadata.reflect(bind=engine)\n\n\n<span class=\"hljs-meta\">@app.route(\"\/people\/&lt;int:id&gt;\", methods=&#91;\"DELETE\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">delete<\/span><span class=\"hljs-params\">(id: int)<\/span> -&gt; Response:<\/span>\n    people_table = metadata.tables&#91;<span class=\"hljs-string\">\"people\"<\/span>]\n    connection.execute(\n        sqlalchemy.delete(people_table).where(\n            people_table.c.id == id\n        )\n    )\n    <span class=\"hljs-keyword\">return<\/span> Response(status=<span class=\"hljs-number\">204<\/span>)  <span class=\"hljs-comment\"># No Content<\/span>\n\n\n<span class=\"hljs-keyword\">if<\/span> __name__ == <span class=\"hljs-string\">\"__main__\"<\/span>:\n    app.run()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>To summarize, the four endpoints you\u2019re most likely to have in an API per (non-junction) table are:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-string\">`\/people`<\/span> <span class=\"hljs-keyword\">with<\/span> GET,\n<span class=\"hljs-string\">`\/people`<\/span> <span class=\"hljs-keyword\">with<\/span> POST,\n<span class=\"hljs-string\">`\/people\/&lt;int: id&gt;`<\/span> <span class=\"hljs-keyword\">with<\/span> PATCH, and \n<span class=\"hljs-string\">`\/people\/&lt;int: id&gt;`<\/span> <span class=\"hljs-keyword\">with<\/span> DELETE<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>And the same four endpoints (with a different url based on the table name) for each of our other (non-junction) tables: bills and events.<\/p>\n\n\n\n<p>Notice anything strange? They all are <code>\/people<\/code>! Why? URLs are broken down into several parts &#8211; the IP (internet protocol) which is, in a browser, going to be http or https, the domain (which is where the API is hosted &#8211; like how <a href=\"http:\/\/www.google.com\" target=\"_blank\" rel=\"noopener\"><code>www.google.com<\/code><\/a> is the domain for Google\u2019s main page), then the API endpoint (after a <code>\/<\/code>). If you turn your eyes to the URL in your browser right now you should see at least <a href=\"https:\/\/coderpad.io\/blog\/\"><code>https:\/\/coderpad.io\/blog\/<\/code><\/a>, meaning the endpoint is <code>\/blog\/<\/code> and a GET verb.<\/p>\n\n\n\n<p>Because of this, the way endpoints are documented uses just the URL (sometimes called a path or route) and the verb because they are unique to our API, whereas we could have our API hosted in multiple places on different protocols.<\/p>\n\n\n\n<p>These four endpoints are a great start to understanding REST APIs, but from here you may start to think of other things you want the API to do. For example, what if you only want information on one person? To GET only one person (one resource) we employ the same tactic of putting the ID in the URL, and we end up with <code>\/people\/&lt;int: id&gt;<\/code> with the GET verb.<\/p>\n\n\n\n<p>Here you could change the response to not be a list since you\u2019re always only returning one resource. However, it can be nice for it to remain a list because then the \u2018instance\u2019 (returning only one resource) endpoint returns in the same format as the \u2018collection\u2019 (returning one or more resources) endpoint, and because of this consistency, the client\u2019s code can reuse the same parsing functions.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> sqlalchemy\n<span class=\"hljs-keyword\">from<\/span> sqlalchemy.orm <span class=\"hljs-keyword\">import<\/span> sessionmaker\n<span class=\"hljs-keyword\">from<\/span> flask <span class=\"hljs-keyword\">import<\/span> Flask\n<span class=\"hljs-keyword\">from<\/span> typing <span class=\"hljs-keyword\">import<\/span> Dict\n\napp = Flask(__name__)\n\n<span class=\"hljs-comment\"># Connect to the DB and reflect metadata.<\/span>\nengine = sqlalchemy.create_engine(<span class=\"hljs-string\">\"postgresql:\/\/coderpad:@\/coderpad?host=\/tmp\/postgresql\/socket\"<\/span>)\nconnection = engine.connect()\nSession = sessionmaker(bind=engine)\nsession = Session()\nmetadata = sqlalchemy.MetaData()\nmetadata.reflect(bind=engine)\n\n\n<span class=\"hljs-meta\">@app.route(\"\/people\/&lt;int:id&gt;\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_one<\/span><span class=\"hljs-params\">(id: int)<\/span> -&gt; Dict:<\/span>\n    people_table = metadata.tables&#91;<span class=\"hljs-string\">\"people\"<\/span>]\n    result = connection.execute(\n        people_table.select().where(people_table.c.id == id)\n    ).fetchall()\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: &#91;\n            dict(person) <span class=\"hljs-keyword\">for<\/span> person <span class=\"hljs-keyword\">in<\/span> result\n        ]\n    }\n\n\n<span class=\"hljs-keyword\">if<\/span> __name__ == <span class=\"hljs-string\">\"__main__\"<\/span>:\n    app.run()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" id=\"present-relationships-rest-api\">How to represent relationships in a REST API<\/h2>\n\n\n\n<p>So far we\u2019ve covered how to give users create, read, update, and delete access to data in all our non-junction tables, but we haven\u2019t covered how to change the relationships between the data. Here\u2019s where APIs get a little more complex, because there is no longer a one table to four (one per verb) endpoints mapping.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"many-to-many-dynamic-routes\">Many-to-many relationships require dynamic routes<\/h3>\n\n\n\n<p>What if you want to know who has signed up for what event? This is a many-to-many relationship (as many people can be signed up for one event, and one person can be signed up for many events) in the junction table, <code>people_events<\/code>, so, to view or change data in many-to-many relationships we need more endpoints.<\/p>\n\n\n\n<p>However, an API endpoint that is <code>people_events<\/code> (with any verb) seems a bit odd. Even if the \u2018user\u2019 is the engineer writing the code to call the API rather than a customer, the user probably doesn\u2019t want to know there\u2019s a table called <code>people_events<\/code>! And the machine definitely doesn\u2019t either. Junction table names are magic that should stay behind the scenes. We want to make the API as intuitive as possible; even if it is intended for machines.<\/p>\n\n\n\n<p>To have these intuitive endpoints, the standard is to add to the existing paths (the URL portion of our endpoints), giving people a \u2018path\u2019 to the related resources. So, for seeing which events someone is signed up for we recycle the path to one person <code>\/people\/&lt;int: id&gt;<\/code> and add <code>\/events<\/code> and <code>\/events\/&lt;int: id&gt;<\/code> onto the end. Then we have both a collection and an instance endpoint.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">\/people\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">people_id<\/span>&gt;<\/span>\/events with GET\n\/people\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">people_id<\/span>&gt;<\/span>\/events\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">event_id<\/span>&gt;<\/span> with GET<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">from<\/span> typing <span class=\"hljs-keyword\">import<\/span> Dict\n<span class=\"hljs-keyword\">import<\/span> sqlalchemy\n<span class=\"hljs-keyword\">from<\/span> sqlalchemy.orm <span class=\"hljs-keyword\">import<\/span> sessionmaker\n<span class=\"hljs-keyword\">from<\/span> flask <span class=\"hljs-keyword\">import<\/span> Flask\n\napp = Flask(__name__)\n\n<span class=\"hljs-comment\"># Connect to the DB and reflect metadata.<\/span>\nengine = sqlalchemy.create_engine(<span class=\"hljs-string\">\"postgresql:\/\/coderpad:@\/coderpad?host=\/tmp\/postgresql\/socket\"<\/span>)\nconnection = engine.connect()\nSession = sessionmaker(bind=engine)\nsession = Session()\nmetadata = sqlalchemy.MetaData()\nmetadata.reflect(bind=engine)\n\n\n<span class=\"hljs-meta\">@app.route(\"\/people\/&lt;int:people_id&gt;\/events\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_people_events_all<\/span><span class=\"hljs-params\">(people_id: int)<\/span> -&gt; Dict:<\/span>\n    people_events_table = metadata.tables&#91;<span class=\"hljs-string\">\"people_events\"<\/span>]\n    events_table = metadata.tables&#91;<span class=\"hljs-string\">\"events\"<\/span>]\n    result = connection.execute(\n        events_table.select().join(\n            people_events_table,\n            events_table.c.id == people_events_table.c.event_id\n        ).where(\n            people_events_table.c.people_id == people_id\n        )\n    ).fetchall()\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: &#91;\n            dict(event) <span class=\"hljs-keyword\">for<\/span> event <span class=\"hljs-keyword\">in<\/span> result\n        ]\n    }\n\n\n<span class=\"hljs-meta\">@app.route(\"\/people\/&lt;int:people_id&gt;\/events\/&lt;int:event_id&gt;\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_people_events_one<\/span><span class=\"hljs-params\">(people_id: int, event_id: int)<\/span> -&gt; Dict:<\/span>\n    people_events_table = metadata.tables&#91;<span class=\"hljs-string\">\"people_events\"<\/span>]\n    events_table = metadata.tables&#91;<span class=\"hljs-string\">\"events\"<\/span>]\n    result = connection.execute(\n        events_table.select().join(\n            people_events_table,\n            events_table.c.id == people_events_table.c.event_id\n        ).where(\n            (people_events_table.c.people_id == people_id)\n            &amp; (people_events_table.c.event_id == event_id)\n        )\n    ).fetchall()\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: &#91;\n            dict(event) <span class=\"hljs-keyword\">for<\/span> event <span class=\"hljs-keyword\">in<\/span> result\n        ]\n    }\n\n\n<span class=\"hljs-keyword\">if<\/span> __name__ == <span class=\"hljs-string\">\"__main__\"<\/span>:\n    app.run()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now the user can see many to many relationships, so the next step is enabling them to edit these relationships. For this type of endpoint, PATCH won\u2019t accept a request \u2018body\u2019 and will instead expect <code>\/people\/&lt;int: people_id&gt;\/events\/&lt;int: event_id&gt;<\/code> where the <code>event_id<\/code> is the ID of an existing event. Then the API will create a relationship where the given person is signed up for the given event. Similarly, DELETE expects <code>\/people\/&lt;int: people_id&gt;\/events\/&lt;int: event_id&gt;<\/code> and only deletes the relationship between the person and the event.<\/p>\n\n\n\n<p>Sometimes you see a POST verb which both creates the resource and the relationship, but it isn\u2019t needed as the same functionality can be achieved by PATCH and POSTing to the <code>\/events<\/code> URL, so for this blog we\u2019ll talk about just PATCH and DELETE.<\/p>\n\n\n\n<p>So all of our sub-endpoints for <code>people<\/code> would look like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">\/people\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">id<\/span>&gt;<\/span>\/events with GET\n\/people\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">id<\/span>&gt;<\/span>\/events\/1 with GET, PATCH, and DELETE<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<p>while the other side of the relationship, <code>events<\/code> would have the endpoints:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">\/events\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">event_id<\/span>&gt;<\/span>\/people\/ with GET\n\/events\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">event_id<\/span>&gt;<\/span>\/people\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">people_id<\/span>&gt;<\/span> with GET, PATCH, and DELETE<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<p>In reality, the instance endpoints provide some redundant functionality &#8211; because relationships go both ways, one side of the relationship\u2019s URLs could be removed and it would be fine. Meaning that<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">\/people\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">people_id<\/span>&gt;<\/span>\/events\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">event_id<\/span>&gt;<\/span> with GET, PATCH, and DELETE<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<p>provides the functionality that can be acquired from<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">\/events\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">event_id<\/span>&gt;<\/span>\/people\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">people_id<\/span>&gt;<\/span> with GET, PATCH, and DELETE<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<p>so only one of the two is needed, but all are included here for completeness\u2019s sake.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">from<\/span> typing <span class=\"hljs-keyword\">import<\/span> Dict\n<span class=\"hljs-keyword\">import<\/span> sqlalchemy\n<span class=\"hljs-keyword\">from<\/span> sqlalchemy.orm <span class=\"hljs-keyword\">import<\/span> sessionmaker\n<span class=\"hljs-keyword\">from<\/span> flask <span class=\"hljs-keyword\">import<\/span> Flask, Response\n\napp = Flask(__name__)\n\n<span class=\"hljs-comment\"># Connect to the DB and reflect metadata.<\/span>\nengine = sqlalchemy.create_engine(<span class=\"hljs-string\">\"postgresql:\/\/coderpad:@\/coderpad?host=\/tmp\/postgresql\/socket\"<\/span>)\nconnection = engine.connect()\nSession = sessionmaker(bind=engine)\nsession = Session()\nmetadata = sqlalchemy.MetaData()\nmetadata.reflect(bind=engine)\n\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">signup_user<\/span><span class=\"hljs-params\">(people_id: int, event_id: int)<\/span> -&gt; <span class=\"hljs-keyword\">None<\/span>:<\/span>\n    people_events_table = metadata.tables&#91;<span class=\"hljs-string\">\"people_events\"<\/span>]\n    connection.execute(\n        sqlalchemy.insert(people_events_table).values(\n            people_id=people_id,\n            event_id=event_id,\n        )\n    )\n\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">delete_signup<\/span><span class=\"hljs-params\">(people_id: int, event_id: int)<\/span> -&gt; <span class=\"hljs-keyword\">None<\/span>:<\/span>\n    people_events_table = metadata.tables&#91;<span class=\"hljs-string\">\"people_events\"<\/span>]\n    connection.execute(\n        sqlalchemy.delete(people_events_table).where(\n            (people_events_table.c.people_id == people_id)\n            &amp; (people_events_table.c.event_id == event_id)\n        )\n    )\n\n\n<span class=\"hljs-meta\">@app.route(\"\/people\/&lt;int:people_id&gt;\/events\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_people_events_all<\/span><span class=\"hljs-params\">(people_id: int)<\/span> -&gt; Dict:<\/span>\n    people_events_table = metadata.tables&#91;<span class=\"hljs-string\">\"people_events\"<\/span>]\n    events_table = metadata.tables&#91;<span class=\"hljs-string\">\"events\"<\/span>]\n    result = connection.execute(\n        events_table.select().join(\n            people_events_table,\n            events_table.c.id == people_events_table.c.event_id\n        ).where(\n            people_events_table.c.people_id == people_id\n        )\n    ).fetchall()\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: &#91;\n            dict(event) <span class=\"hljs-keyword\">for<\/span> event <span class=\"hljs-keyword\">in<\/span> result\n        ]\n    }\n\n\n<span class=\"hljs-meta\">@app.route(\"\/people\/&lt;int:people_id&gt;\/events\/&lt;int:event_id&gt;\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_people_events_one<\/span><span class=\"hljs-params\">(people_id: int, event_id: int)<\/span> -&gt; Dict:<\/span>\n    people_events_table = metadata.tables&#91;<span class=\"hljs-string\">\"people_events\"<\/span>]\n    events_table = metadata.tables&#91;<span class=\"hljs-string\">\"events\"<\/span>]\n    result = connection.execute(\n        events_table.select().join(\n            people_events_table,\n            events_table.c.id == people_events_table.c.event_id\n        ).where(\n            (people_events_table.c.people_id == people_id)\n            &amp; (people_events_table.c.event_id == event_id)\n        )\n    ).fetchall()\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: &#91;\n            dict(event) <span class=\"hljs-keyword\">for<\/span> event <span class=\"hljs-keyword\">in<\/span> result\n        ]\n    }\n\n\n<span class=\"hljs-meta\">@app.route(\"\/events\/&lt;int:event_id&gt;\/people\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_events_people_all<\/span><span class=\"hljs-params\">(event_id: int)<\/span> -&gt; Dict:<\/span>\n    people_events_table = metadata.tables&#91;<span class=\"hljs-string\">\"people_events\"<\/span>]\n    people_table = metadata.tables&#91;<span class=\"hljs-string\">\"people\"<\/span>]\n    result = connection.execute(\n        people_table.select().join(\n            people_events_table,\n            people_table.c.id == people_events_table.c.event_id\n        ).where(\n            people_events_table.c.event_id == event_id\n        )\n    ).fetchall()\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: &#91;\n            dict(event) <span class=\"hljs-keyword\">for<\/span> event <span class=\"hljs-keyword\">in<\/span> result\n        ]\n    }\n\n\n<span class=\"hljs-meta\">@app.route(\"\/events\/&lt;int:event_id&gt;\/people\/&lt;int:people_id&gt;\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_events_people_one<\/span><span class=\"hljs-params\">(event_id: int, people_id: int)<\/span> -&gt; Dict:<\/span>\n    people_events_table = metadata.tables&#91;<span class=\"hljs-string\">\"people_events\"<\/span>]\n    people_table = metadata.tables&#91;<span class=\"hljs-string\">\"people\"<\/span>]\n    result = connection.execute(\n        people_table.select().join(\n            people_events_table,\n            people_table.c.id == people_events_table.c.event_id\n        ).where(\n            (people_events_table.c.people_id == people_id)\n            &amp; (people_events_table.c.event_id == event_id)\n        )\n    ).fetchall()\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: &#91;\n            dict(event) <span class=\"hljs-keyword\">for<\/span> event <span class=\"hljs-keyword\">in<\/span> result\n        ]\n    }\n\n\n<span class=\"hljs-meta\">@app.route(\"\/people\/&lt;int:people_id&gt;\/events\/&lt;int:event_id&gt;\", methods=&#91;\"PATCH\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">patch_people_events<\/span><span class=\"hljs-params\">(people_id: int, event_id: int)<\/span> -&gt; Response:<\/span>\n    <span class=\"hljs-comment\"># Ideally there'd be error handling for if the event doesn't exist here<\/span>\n\n    <span class=\"hljs-comment\"># Sign person up for event<\/span>\n    signup_user(people_id, event_id)\n    <span class=\"hljs-keyword\">return<\/span> Response(status=<span class=\"hljs-number\">204<\/span>)  <span class=\"hljs-comment\"># No Content<\/span>\n\n\n<span class=\"hljs-meta\">@app.route(\"\/events\/&lt;int:event_id&gt;\/people\/&lt;int:people_id&gt;\", methods=&#91;\"PATCH\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">patch_events_people<\/span><span class=\"hljs-params\">(event_id: int, people_id: int)<\/span> -&gt; Response:<\/span>\n    <span class=\"hljs-comment\"># Ideally there'd be error handling for if the event doesn't exist here<\/span>\n\n    <span class=\"hljs-comment\"># Sign person up for event<\/span>\n    signup_user(people_id, event_id)\n    <span class=\"hljs-keyword\">return<\/span> Response(status=<span class=\"hljs-number\">204<\/span>)  <span class=\"hljs-comment\"># No Content<\/span>\n\n\n<span class=\"hljs-meta\">@app.route(\"\/people\/&lt;int:people_id&gt;\/events\/&lt;int:event_id&gt;\", methods=&#91;\"DELETE\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">delete_people_events<\/span><span class=\"hljs-params\">(people_id: int, event_id: int)<\/span> -&gt; Response:<\/span>\n    delete_signup(people_id, event_id)\n    <span class=\"hljs-keyword\">return<\/span> Response(status=<span class=\"hljs-number\">204<\/span>)  <span class=\"hljs-comment\"># No Content<\/span>\n\n\n<span class=\"hljs-meta\">@app.route(\"\/events\/&lt;int:event_id&gt;\/people\/&lt;int:people_id&gt;\", methods=&#91;\"DELETE\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">delete_events_people<\/span><span class=\"hljs-params\">(event_id: int, people_id: int)<\/span> -&gt; Response:<\/span>\n    delete_signup(people_id, event_id)\n    <span class=\"hljs-keyword\">return<\/span> Response(status=<span class=\"hljs-number\">204<\/span>)  <span class=\"hljs-comment\"># No Content<\/span>\n\n\n<span class=\"hljs-keyword\">if<\/span> __name__ == <span class=\"hljs-string\">\"__main__\"<\/span>:\n    app.run()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\" id=\"one-to-one-sub-endpoints\">One to One relationships don\u2019t need sub-endpoints<\/h3>\n\n\n\n<p>The same concept can also be applied for 1-1 relationships, but when there\u2019s only ever one relationship it doesn\u2019t make as much sense to make their own endpoints, so instead a \u2018links\u2019 field is often added to a resource.<\/p>\n\n\n\n<p>Meaning that the return of <code>\/events\/&lt;int: events_id&gt;<\/code> changes from<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json shcb-wrap-lines\">{\n   <span class=\"hljs-attr\">\"result\"<\/span>:&#91;\n      {\n         <span class=\"hljs-attr\">\"date\"<\/span>:<span class=\"hljs-string\">\"Thu, 30 Jun 2022 00:00:00 GMT\"<\/span>,\n         <span class=\"hljs-attr\">\"id\"<\/span>:<span class=\"hljs-number\">2<\/span>,\n         <span class=\"hljs-attr\">\"type\"<\/span>:<span class=\"hljs-string\">\"conference\"<\/span>\n      }\n   ]\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>to<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json shcb-wrap-lines\">{\n   <span class=\"hljs-attr\">\"result\"<\/span>:&#91;\n      {\n         <span class=\"hljs-attr\">\"date\"<\/span>:<span class=\"hljs-string\">\"Sat, 20 Dec 2025 00:00:00 GMT\"<\/span>,\n         <span class=\"hljs-attr\">\"id\"<\/span>:<span class=\"hljs-number\">3<\/span>,\n         <span class=\"hljs-attr\">\"links\"<\/span>:{\n            <span class=\"hljs-attr\">\"bill\"<\/span>:<span class=\"hljs-string\">\"\/bills\/3\"<\/span>\n         },\n         <span class=\"hljs-attr\">\"type\"<\/span>:<span class=\"hljs-string\">\"holiday party\"<\/span>\n      }\n   ]\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">from<\/span> typing <span class=\"hljs-keyword\">import<\/span> Dict\n<span class=\"hljs-keyword\">import<\/span> sqlalchemy\n<span class=\"hljs-keyword\">from<\/span> sqlalchemy.orm <span class=\"hljs-keyword\">import<\/span> sessionmaker\n<span class=\"hljs-keyword\">from<\/span> flask <span class=\"hljs-keyword\">import<\/span> Flask\n\napp = Flask(__name__)\n\n<span class=\"hljs-comment\"># Connect to the DB and reflect metadata.<\/span>\nengine = sqlalchemy.create_engine(<span class=\"hljs-string\">\"postgresql:\/\/coderpad:@\/coderpad?host=\/tmp\/postgresql\/socket\"<\/span>)\nconnection = engine.connect()\nSession = sessionmaker(bind=engine)\nsession = Session()\nmetadata = sqlalchemy.MetaData()\nmetadata.reflect(bind=engine)\n\n\n<span class=\"hljs-meta\">@app.route(\"\/events\/&lt;int:id&gt;\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_one_events_with_links<\/span><span class=\"hljs-params\">(id: int)<\/span> -&gt; Dict:<\/span>\n    <span class=\"hljs-comment\"># Ideally you'd simplify this so you aren't making so many Database calls,<\/span>\n    <span class=\"hljs-comment\"># but it's left this way for clarity.<\/span>\n    events_table = metadata.tables&#91;<span class=\"hljs-string\">\"events\"<\/span>]\n    bills_table = metadata.tables&#91;<span class=\"hljs-string\">\"bills\"<\/span>]\n    result = connection.execute(\n        sqlalchemy.select(events_table).where(\n            events_table.c.id == id\n        )\n    ).fetchall()\n    events = &#91;]\n    <span class=\"hljs-keyword\">for<\/span> event <span class=\"hljs-keyword\">in<\/span> result:\n        event = dict(event)\n\n        <span class=\"hljs-comment\"># Get related bill<\/span>\n        bill = connection.execute(\n            sqlalchemy.select(bills_table).where(\n                bills_table.c.event_id == id\n            )\n        ).fetchall()\n        <span class=\"hljs-keyword\">if<\/span> len(bill) &gt; <span class=\"hljs-number\">0<\/span>:\n            bill_id = dict(bill&#91;<span class=\"hljs-number\">0<\/span>]).get(<span class=\"hljs-string\">\"id\"<\/span>)\n            event&#91;<span class=\"hljs-string\">\"links\"<\/span>] = {\n                <span class=\"hljs-string\">\"bill\"<\/span>: <span class=\"hljs-string\">\"\/bills\/{}\"<\/span>.format(bill_id)\n            }\n        events.append(event)\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: events\n    }\n\n\n<span class=\"hljs-keyword\">if<\/span> __name__ == <span class=\"hljs-string\">\"__main__\"<\/span>:\n    app.run()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\" id=\"one-to-many-combined-approaches\">One to Many relationships combine One to One and Many to Many approaches<\/h3>\n\n\n\n<p>For one to many relationships, such as how one person can have multiple bills, but one bill only has one person who has to pay it, we combine both concepts. From the side of a resource with many related concepts (people) we reuse the same concept as many-to-many, leaving us with the endpoints:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">\/people\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">people_id<\/span>&gt;<\/span>\/bills\/ with GET\n\/people\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">people_id<\/span>&gt;<\/span>\/bills\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">bill_id<\/span>&gt;<\/span> with GET, PATCH, and DELETE<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<p>However, \u201cPATCH\u201d and \u201cDELETE\u201d are also redundant since any editing of \u2018bills\u2018 can be done on the <code>\/bills\/&lt;int: bill_id&gt;<\/code> endpoints, so we can ignore those verbs here.<\/p>\n\n\n\n<p>To finish off the people to bills relationship, we reuse the 1-1 concept of a `links` field for the other side of the equation, meaning that the return of <code>\/bills\/&lt;int: id&gt;<\/code> changes from<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json shcb-wrap-lines\">{\n   <span class=\"hljs-attr\">\"result\"<\/span>:&#91;\n      {\n         <span class=\"hljs-attr\">\"cost\"<\/span>:<span class=\"hljs-number\">10000<\/span>,\n         <span class=\"hljs-attr\">\"due\"<\/span>:<span class=\"hljs-string\">\"Tue, 01 Jun 2021 00:00:00 GMT\"<\/span>,\n         <span class=\"hljs-attr\">\"event_id\"<\/span>:<span class=\"hljs-number\">2<\/span>,\n         <span class=\"hljs-attr\">\"id\"<\/span>:<span class=\"hljs-number\">2<\/span>,\n         <span class=\"hljs-attr\">\"paid\"<\/span>:<span class=\"hljs-string\">\"not paid\"<\/span>,\n         <span class=\"hljs-attr\">\"payer_id\"<\/span>:<span class=\"hljs-number\">1<\/span>\n      }\n   ]\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<p>to<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json shcb-wrap-lines\">{\n   <span class=\"hljs-attr\">\"result\"<\/span>:&#91;\n      {\n         <span class=\"hljs-attr\">\"cost\"<\/span>:<span class=\"hljs-number\">10000<\/span>,\n         <span class=\"hljs-attr\">\"due\"<\/span>:<span class=\"hljs-string\">\"Tue, 01 Jun 2021 00:00:00 GMT\"<\/span>,\n         <span class=\"hljs-attr\">\"event_id\"<\/span>:<span class=\"hljs-number\">2<\/span>,\n         <span class=\"hljs-attr\">\"id\"<\/span>:<span class=\"hljs-number\">2<\/span>,\n         <span class=\"hljs-attr\">\"links\"<\/span>:{\n            <span class=\"hljs-attr\">\"people\"<\/span>:<span class=\"hljs-string\">\"\/people\/1\"<\/span>\n         },\n         <span class=\"hljs-attr\">\"paid\"<\/span>:<span class=\"hljs-string\">\"not paid\"<\/span>,\n         <span class=\"hljs-attr\">\"payer_id\"<\/span>:<span class=\"hljs-number\">1<\/span>\n      }\n   ]\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">from<\/span> typing <span class=\"hljs-keyword\">import<\/span> Dict\n<span class=\"hljs-keyword\">import<\/span> sqlalchemy\n<span class=\"hljs-keyword\">from<\/span> sqlalchemy.orm <span class=\"hljs-keyword\">import<\/span> sessionmaker\n<span class=\"hljs-keyword\">from<\/span> flask <span class=\"hljs-keyword\">import<\/span> Flask\n\napp = Flask(__name__)\n\n<span class=\"hljs-comment\"># Connect to the DB and reflect metadata.<\/span>\nengine = sqlalchemy.create_engine(<span class=\"hljs-string\">\"postgresql:\/\/coderpad:@\/coderpad?host=\/tmp\/postgresql\/socket\"<\/span>)\nconnection = engine.connect()\nSession = sessionmaker(bind=engine)\nsession = Session()\nmetadata = sqlalchemy.MetaData()\nmetadata.reflect(bind=engine)\n\n\n<span class=\"hljs-meta\">@app.route(\"\/events\/&lt;int:id&gt;\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_one_events_with_links<\/span><span class=\"hljs-params\">(id: int)<\/span> -&gt; Dict:<\/span>\n    <span class=\"hljs-comment\"># Ideally you'd simplify this so you aren't making so many Database calls,<\/span>\n    <span class=\"hljs-comment\"># but it's left this way for clarity.<\/span>\n    events_table = metadata.tables&#91;<span class=\"hljs-string\">\"events\"<\/span>]\n    bills_table = metadata.tables&#91;<span class=\"hljs-string\">\"bills\"<\/span>]\n    result = connection.execute(\n        sqlalchemy.select(events_table).where(\n            events_table.c.id == id\n        )\n    ).fetchall()\n    events = &#91;]\n    <span class=\"hljs-keyword\">for<\/span> event <span class=\"hljs-keyword\">in<\/span> result:\n        event = dict(event)\n\n        <span class=\"hljs-comment\"># Get related bill<\/span>\n        bill = connection.execute(\n            sqlalchemy.select(bills_table).where(\n                bills_table.c.event_id == id\n            )\n        ).fetchall()\n        <span class=\"hljs-keyword\">if<\/span> len(bill) &gt; <span class=\"hljs-number\">0<\/span>:\n            bill_id = dict(bill&#91;<span class=\"hljs-number\">0<\/span>]).get(<span class=\"hljs-string\">\"id\"<\/span>)\n            event&#91;<span class=\"hljs-string\">\"links\"<\/span>] = {\n                <span class=\"hljs-string\">\"bill\"<\/span>: <span class=\"hljs-string\">\"\/bills\/{}\"<\/span>.format(bill_id)\n            }\n        events.append(event)\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: events\n    }\n\n\n<span class=\"hljs-meta\">@app.route(\"\/bills\/&lt;int:id&gt;\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_one_bills<\/span><span class=\"hljs-params\">(id: int)<\/span> -&gt; Dict:<\/span>\n    people_table = metadata.tables&#91;<span class=\"hljs-string\">\"people\"<\/span>]\n    bills_table = metadata.tables&#91;<span class=\"hljs-string\">\"bills\"<\/span>]\n    result = connection.execute(\n        sqlalchemy.select(bills_table).where(\n            bills_table.c.id == id\n        )\n    ).fetchall()\n    bills = &#91;]\n    <span class=\"hljs-keyword\">for<\/span> bill <span class=\"hljs-keyword\">in<\/span> result:\n        bill = dict(bill)\n\n        <span class=\"hljs-comment\"># Get related person<\/span>\n        person = connection.execute(\n            sqlalchemy.select(people_table).where(\n                people_table.c.id == bill.get(<span class=\"hljs-string\">\"payer_id\"<\/span>)\n            )\n        ).fetchall()\n        <span class=\"hljs-keyword\">if<\/span> len(person) &gt; <span class=\"hljs-number\">0<\/span>:\n            people_id = dict(person&#91;<span class=\"hljs-number\">0<\/span>]).get(<span class=\"hljs-string\">\"id\"<\/span>)\n            bill&#91;<span class=\"hljs-string\">\"links\"<\/span>] = {\n                <span class=\"hljs-string\">\"people\"<\/span>: <span class=\"hljs-string\">\"\/people\/{}\"<\/span>.format(people_id)\n            }\n        bills.append(bill)\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: bills\n    }\n\n\n<span class=\"hljs-keyword\">if<\/span> __name__ == <span class=\"hljs-string\">\"__main__\"<\/span>:\n    app.run()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" id=\"all-api-endpoints\">All API Endpoints<\/h2>\n\n\n\n<p>To summarize, for our four database tables (<code>people<\/code>, <code>events<\/code>, <code>bills<\/code>, and <code>people_events<\/code>, our API looks like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">\/people with GET\n\/people\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:id<\/span>&gt;<\/span> with GET\n\/people with POST\n\/people\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">id<\/span>&gt;<\/span> with PATCH\n\/people\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">id<\/span>&gt;<\/span> with DELETE\n\/people\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">people_id<\/span>&gt;<\/span>\/events\/ with GET\n\/people\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">people_id<\/span>&gt;<\/span>\/events\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">event_id<\/span>&gt;<\/span> with GET, PATCH, and DELETE\n\/people\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">people_id<\/span>&gt;<\/span>\/bills\/ with GET\n\/people\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">people_id<\/span>&gt;<\/span>\/bills\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">bill_id<\/span>&gt;<\/span> with GET\n\n\/events with GET\n\/events\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:id<\/span>&gt;<\/span> with GET\n\/events with POST\n\/events\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">id<\/span>&gt;<\/span> with PATCH\n\/events\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">id<\/span>&gt;<\/span> with DELETE\n\/events\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">event_id<\/span>&gt;<\/span>\/people\/ with GET\n\/events\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">event_id<\/span>&gt;<\/span>\/people\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">people_id<\/span>&gt;<\/span> with GET, PATCH, and DELETE\n\n\/bills with GET\n\/bills\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:id<\/span>&gt;<\/span> with GET\n\/bills with POST\n\/bills\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">id<\/span>&gt;<\/span> with PATCH\n\/bills\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int:<\/span> <span class=\"hljs-attr\">id<\/span>&gt;<\/span> with DELETE<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<p>And the code for it all looks like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> sqlalchemy\n<span class=\"hljs-keyword\">from<\/span> sqlalchemy.orm <span class=\"hljs-keyword\">import<\/span> sessionmaker\n<span class=\"hljs-keyword\">from<\/span> flask <span class=\"hljs-keyword\">import<\/span> Flask, Response, request\n<span class=\"hljs-keyword\">from<\/span> typing <span class=\"hljs-keyword\">import<\/span> Any, Dict\n\napp = Flask(__name__)\n\n<span class=\"hljs-comment\"># Connect to the DB and reflect metadata.<\/span>\nengine = sqlalchemy.create_engine(<span class=\"hljs-string\">\"postgresql:\/\/coderpad:@\/coderpad?host=\/tmp\/postgresql\/socket\"<\/span>)\nconnection = engine.connect()\nSession = sessionmaker(bind=engine)\nsession = Session()\nmetadata = sqlalchemy.MetaData()\nmetadata.reflect(bind=engine)\n\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_all<\/span><span class=\"hljs-params\">(table_name: str)<\/span> -&gt; Dict:<\/span>\n    table = metadata.tables&#91;table_name]\n    result = connection.execute(table.select()).fetchall()\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: &#91;\n            dict(resource) <span class=\"hljs-keyword\">for<\/span> resource <span class=\"hljs-keyword\">in<\/span> result\n        ]\n    }\n\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_one<\/span><span class=\"hljs-params\">(table_name: str, column_name: str, value: Any)<\/span> -&gt; Dict:<\/span>\n    table = metadata.tables&#91;table_name]\n    result = connection.execute(\n        sqlalchemy.select(table).where(\n            getattr(table.c, column_name) == value\n        )\n    ).fetchall()\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: &#91;\n            dict(resource) <span class=\"hljs-keyword\">for<\/span> resource <span class=\"hljs-keyword\">in<\/span> result\n        ]\n    }\n\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">patch_one<\/span><span class=\"hljs-params\">(table_name: str, column_name: str, value: Any)<\/span> -&gt; Response:<\/span>\n    content_type = request.headers.get(<span class=\"hljs-string\">\"Content-Type\"<\/span>)\n    <span class=\"hljs-keyword\">if<\/span> (content_type != <span class=\"hljs-string\">\"application\/json\"<\/span>):\n        <span class=\"hljs-keyword\">return<\/span> {\n            <span class=\"hljs-string\">\"result\"<\/span>: <span class=\"hljs-string\">\"Failure\"<\/span>,\n            <span class=\"hljs-string\">\"reason\"<\/span>: <span class=\"hljs-string\">\"Content-Type not supported!\"<\/span>,\n        }\n\n    table = metadata.tables&#91;table_name]\n    <span class=\"hljs-comment\"># There should be validation that the PATCH body is valid here!<\/span>\n    connection.execute(\n        sqlalchemy.update(table).where(\n            getattr(table.c, column_name) == value\n        ).values(\n            **request.json,\n        )\n    )\n    <span class=\"hljs-keyword\">return<\/span> Response(status=<span class=\"hljs-number\">204<\/span>)  <span class=\"hljs-comment\"># No Content<\/span>\n\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">post<\/span><span class=\"hljs-params\">(table_name: str)<\/span> -&gt; Response:<\/span>\n    content_type = request.headers.get(<span class=\"hljs-string\">\"Content-Type\"<\/span>)\n    <span class=\"hljs-keyword\">if<\/span> (content_type != <span class=\"hljs-string\">\"application\/json\"<\/span>):\n        <span class=\"hljs-keyword\">return<\/span> {\n            <span class=\"hljs-string\">\"result\"<\/span>: <span class=\"hljs-string\">\"Failure\"<\/span>,\n            <span class=\"hljs-string\">\"reason\"<\/span>: <span class=\"hljs-string\">\"Content-Type not supported!\"<\/span>,\n        }\n\n    table = metadata.tables&#91;table_name]\n    <span class=\"hljs-comment\"># There should be validation that the POST body is valid here!<\/span>\n    connection.execute(\n        sqlalchemy.insert(table).values(\n            **request.json,\n        )\n    )\n    <span class=\"hljs-keyword\">return<\/span> Response(status=<span class=\"hljs-number\">201<\/span>)  <span class=\"hljs-comment\"># Created<\/span>\n\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">delete_one<\/span><span class=\"hljs-params\">(table_name: str, column_name: str, value: Any)<\/span> -&gt; Response:<\/span>\n    table = metadata.tables&#91;table_name]\n    connection.execute(\n        sqlalchemy.delete(table).where(\n            getattr(table.c, column_name) == value\n        )\n    )\n    <span class=\"hljs-keyword\">return<\/span> Response(status=<span class=\"hljs-number\">204<\/span>)  <span class=\"hljs-comment\"># No Content<\/span>\n\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">signup_user<\/span><span class=\"hljs-params\">(people_id: int, event_id: int)<\/span> -&gt; <span class=\"hljs-keyword\">None<\/span>:<\/span>\n    people_events_table = metadata.tables&#91;<span class=\"hljs-string\">\"people_events\"<\/span>]\n    connection.execute(\n        sqlalchemy.insert(people_events_table).values(\n            people_id=people_id,\n            event_id=event_id,\n        )\n    )\n\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">delete_signup<\/span><span class=\"hljs-params\">(people_id: int, event_id: int)<\/span> -&gt; <span class=\"hljs-keyword\">None<\/span>:<\/span>\n    people_events_table = metadata.tables&#91;<span class=\"hljs-string\">\"people_events\"<\/span>]\n    connection.execute(\n        sqlalchemy.delete(people_events_table).where(\n            (people_events_table.c.people_id == people_id)\n            &amp; (people_events_table.c.event_id == event_id)\n        )\n    )\n\n\n<span class=\"hljs-comment\"># Endpoints:<\/span>\n\n<span class=\"hljs-meta\">@app.route(\"\/people\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_all_people<\/span><span class=\"hljs-params\">()<\/span> -&gt; Dict:<\/span>\n    <span class=\"hljs-keyword\">return<\/span> get_all(table_name=<span class=\"hljs-string\">\"people\"<\/span>)\n\n\n<span class=\"hljs-meta\">@app.route(\"\/people\/&lt;int:id&gt;\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_one_people<\/span><span class=\"hljs-params\">(id: int)<\/span> -&gt; Dict:<\/span>\n    <span class=\"hljs-keyword\">return<\/span> get_one(\n        table_name=<span class=\"hljs-string\">\"people\"<\/span>,\n        column_name=<span class=\"hljs-string\">\"id\"<\/span>,\n        value=id,\n    )\n\n\n<span class=\"hljs-meta\">@app.route(\"\/people\", methods=&#91;\"POST\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">post_people<\/span><span class=\"hljs-params\">()<\/span>  -&gt; Response:<\/span>\n    <span class=\"hljs-keyword\">return<\/span> post(<span class=\"hljs-string\">\"people\"<\/span>)\n\n\n<span class=\"hljs-meta\">@app.route(\"\/people\/&lt;int:id&gt;\", methods=&#91;\"PATCH\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">patch_people<\/span><span class=\"hljs-params\">(id: int)<\/span> -&gt; Response:<\/span>\n    <span class=\"hljs-keyword\">return<\/span> patch_one(\n        table_name=<span class=\"hljs-string\">\"people\"<\/span>,\n        column_name=<span class=\"hljs-string\">\"id\"<\/span>,\n        value=id,\n    )\n\n\n<span class=\"hljs-meta\">@app.route(\"\/people\/&lt;int:id&gt;\", methods=&#91;\"DELETE\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">delete_people<\/span><span class=\"hljs-params\">(id: int)<\/span> -&gt; Response:<\/span>\n    <span class=\"hljs-comment\"># Should cascade deleting relationship(s) in `people_events` here<\/span>\n    <span class=\"hljs-comment\"># Should also cascade deleting related `bill`(s) here<\/span>\n    <span class=\"hljs-keyword\">return<\/span> delete_one(\n        table_name=<span class=\"hljs-string\">\"people\"<\/span>,\n        column_name=<span class=\"hljs-string\">\"id\"<\/span>,\n        value=id,\n    )\n\n\n<span class=\"hljs-comment\"># Without 'links'<\/span>\n<span class=\"hljs-comment\"># @app.route(\"\/events\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-comment\"># def get_all_events() -&gt; Dict:<\/span>\n<span class=\"hljs-comment\">#     return get_all(table_name=\"events\")<\/span>\n\n\n<span class=\"hljs-meta\">@app.route(\"\/events\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_all_events<\/span><span class=\"hljs-params\">()<\/span> -&gt; Dict:<\/span>\n    <span class=\"hljs-comment\"># Ideally you'd simplify this so you aren't making so many Database calls,<\/span>\n    <span class=\"hljs-comment\"># but it's left this way for clarity.<\/span>\n    events_table = metadata.tables&#91;<span class=\"hljs-string\">\"events\"<\/span>]\n    bills_table = metadata.tables&#91;<span class=\"hljs-string\">\"bills\"<\/span>]\n    result = connection.execute(events_table.select()).fetchall()\n    events = &#91;]\n    <span class=\"hljs-keyword\">for<\/span> event <span class=\"hljs-keyword\">in<\/span> result:\n        event = dict(event)\n\n        <span class=\"hljs-comment\"># Get related bill<\/span>\n        bill = connection.execute(\n            sqlalchemy.select(bills_table).where(\n                bills_table.c.event_id == event.get(<span class=\"hljs-string\">\"id\"<\/span>)\n            )\n        ).fetchall()\n        <span class=\"hljs-keyword\">if<\/span> len(bill) &gt; <span class=\"hljs-number\">0<\/span>:\n            bill_id = dict(bill&#91;<span class=\"hljs-number\">0<\/span>]).get(<span class=\"hljs-string\">\"id\"<\/span>)\n            event&#91;<span class=\"hljs-string\">\"links\"<\/span>] = {\n                <span class=\"hljs-string\">\"bill\"<\/span>: <span class=\"hljs-string\">\"\/bills\/{}\"<\/span>.format(bill_id)\n            }\n        events.append(event)\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: events\n    }\n\n\n<span class=\"hljs-comment\"># Without 'links'<\/span>\n<span class=\"hljs-comment\"># @app.route(\"\/events\/&lt;int:id&gt;\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-comment\"># def get_one_events(id: int) -&gt; Dict:<\/span>\n<span class=\"hljs-comment\">#     return get_one(<\/span>\n<span class=\"hljs-comment\">#         table_name=\"events\",<\/span>\n<span class=\"hljs-comment\">#         column_name=\"id\",<\/span>\n<span class=\"hljs-comment\">#         value=id,<\/span>\n<span class=\"hljs-comment\">#     )<\/span>\n\n\n<span class=\"hljs-meta\">@app.route(\"\/events\/&lt;int:id&gt;\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_one_events_with_links<\/span><span class=\"hljs-params\">(id: int)<\/span> -&gt; Dict:<\/span>\n    <span class=\"hljs-comment\"># Ideally you'd simplify this so you aren't making so many Database calls,<\/span>\n    <span class=\"hljs-comment\"># but it's left this way for clarity.<\/span>\n    events_table = metadata.tables&#91;<span class=\"hljs-string\">\"events\"<\/span>]\n    bills_table = metadata.tables&#91;<span class=\"hljs-string\">\"bills\"<\/span>]\n    result = connection.execute(\n        sqlalchemy.select(events_table).where(\n            events_table.c.id == id\n        )\n    ).fetchall()\n    events = &#91;]\n    <span class=\"hljs-keyword\">for<\/span> event <span class=\"hljs-keyword\">in<\/span> result:\n        event = dict(event)\n\n        <span class=\"hljs-comment\"># Get related bill<\/span>\n        bill = connection.execute(\n            sqlalchemy.select(bills_table).where(\n                bills_table.c.event_id == id\n            )\n        ).fetchall()\n        <span class=\"hljs-keyword\">if<\/span> len(bill) &gt; <span class=\"hljs-number\">0<\/span>:\n            bill_id = dict(bill&#91;<span class=\"hljs-number\">0<\/span>]).get(<span class=\"hljs-string\">\"id\"<\/span>)\n            event&#91;<span class=\"hljs-string\">\"links\"<\/span>] = {\n                <span class=\"hljs-string\">\"bill\"<\/span>: <span class=\"hljs-string\">\"\/bills\/{}\"<\/span>.format(bill_id)\n            }\n        events.append(event)\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: events\n    }\n\n\n<span class=\"hljs-meta\">@app.route(\"\/events\", methods=&#91;\"POST\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">post_events<\/span><span class=\"hljs-params\">()<\/span> -&gt; Response:<\/span>\n    <span class=\"hljs-keyword\">return<\/span> post(<span class=\"hljs-string\">\"events\"<\/span>)\n\n\n<span class=\"hljs-meta\">@app.route(\"\/events\/&lt;int:id&gt;\", methods=&#91;\"PATCH\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">patch_events<\/span><span class=\"hljs-params\">(id: int)<\/span> -&gt; Response:<\/span>\n    <span class=\"hljs-keyword\">return<\/span> patch_one(\n        table_name=<span class=\"hljs-string\">\"events\"<\/span>,\n        column_name=<span class=\"hljs-string\">\"id\"<\/span>,\n        value=id,\n    )\n\n\n<span class=\"hljs-meta\">@app.route(\"\/events\/&lt;int:id&gt;\", methods=&#91;\"DELETE\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">delete_events<\/span><span class=\"hljs-params\">(id: int)<\/span> -&gt; Response:<\/span>\n    <span class=\"hljs-comment\"># Should cascade deleting relationship(s) in `people_events` here<\/span>\n    <span class=\"hljs-comment\"># Should also cascade deleting related `bill` here<\/span>\n    <span class=\"hljs-keyword\">return<\/span> delete_one(\n        table_name=<span class=\"hljs-string\">\"events\"<\/span>,\n        column_name=<span class=\"hljs-string\">\"id\"<\/span>,\n        value=id,\n    )\n\n\n<span class=\"hljs-comment\"># Without 'links'<\/span>\n<span class=\"hljs-comment\"># @app.route(\"\/bills\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-comment\"># def get_all_bills() -&gt; Dict:<\/span>\n<span class=\"hljs-comment\">#     return get_all(table_name=\"bills\")<\/span>\n\n\n<span class=\"hljs-meta\">@app.route(\"\/bills\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_all_bills<\/span><span class=\"hljs-params\">()<\/span> -&gt; Dict:<\/span>\n    people_table = metadata.tables&#91;<span class=\"hljs-string\">\"people\"<\/span>]\n    bills_table = metadata.tables&#91;<span class=\"hljs-string\">\"bills\"<\/span>]\n    result = connection.execute(bills_table.select()).fetchall()\n    bills = &#91;]\n    <span class=\"hljs-keyword\">for<\/span> bill <span class=\"hljs-keyword\">in<\/span> result:\n        bill = dict(bill)\n\n        <span class=\"hljs-comment\"># Get related person<\/span>\n        person = connection.execute(\n            sqlalchemy.select(people_table).where(\n                people_table.c.id == bill.get(<span class=\"hljs-string\">\"payer_id\"<\/span>)\n            )\n        ).fetchall()\n        <span class=\"hljs-keyword\">if<\/span> len(person) &gt; <span class=\"hljs-number\">0<\/span>:\n            people_id = dict(person&#91;<span class=\"hljs-number\">0<\/span>]).get(<span class=\"hljs-string\">\"id\"<\/span>)\n            bill&#91;<span class=\"hljs-string\">\"links\"<\/span>] = {\n                <span class=\"hljs-string\">\"people\"<\/span>: <span class=\"hljs-string\">\"\/people\/{}\"<\/span>.format(people_id)\n            }\n        bills.append(bill)\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: bills\n    }\n\n\n<span class=\"hljs-comment\"># Without 'links'<\/span>\n<span class=\"hljs-comment\"># @app.route(\"\/bills\/&lt;int:id&gt;\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-comment\"># def get_one_bills(id: int) -&gt; Dict:<\/span>\n<span class=\"hljs-comment\">#     return get_one(<\/span>\n<span class=\"hljs-comment\">#         table_name=\"bills\",<\/span>\n<span class=\"hljs-comment\">#         column_name=\"id\",<\/span>\n<span class=\"hljs-comment\">#         value=id,<\/span>\n<span class=\"hljs-comment\">#     )<\/span>\n\n\n<span class=\"hljs-meta\">@app.route(\"\/bills\/&lt;int:id&gt;\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_one_bills<\/span><span class=\"hljs-params\">(id: int)<\/span> -&gt; Dict:<\/span>\n    people_table = metadata.tables&#91;<span class=\"hljs-string\">\"people\"<\/span>]\n    bills_table = metadata.tables&#91;<span class=\"hljs-string\">\"bills\"<\/span>]\n    result = connection.execute(\n        sqlalchemy.select(bills_table).where(\n            bills_table.c.id == id\n        )\n    ).fetchall()\n    bills = &#91;]\n    <span class=\"hljs-keyword\">for<\/span> bill <span class=\"hljs-keyword\">in<\/span> result:\n        bill = dict(bill)\n\n        <span class=\"hljs-comment\"># Get related person<\/span>\n        person = connection.execute(\n            sqlalchemy.select(people_table).where(\n                people_table.c.id == bill.get(<span class=\"hljs-string\">\"payer_id\"<\/span>)\n            )\n        ).fetchall()\n        <span class=\"hljs-keyword\">if<\/span> len(person) &gt; <span class=\"hljs-number\">0<\/span>:\n            people_id = dict(person&#91;<span class=\"hljs-number\">0<\/span>]).get(<span class=\"hljs-string\">\"id\"<\/span>)\n            bill&#91;<span class=\"hljs-string\">\"links\"<\/span>] = {\n                <span class=\"hljs-string\">\"people\"<\/span>: <span class=\"hljs-string\">\"\/people\/{}\"<\/span>.format(people_id)\n            }\n        bills.append(bill)\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: bills\n    }\n\n\n<span class=\"hljs-meta\">@app.route(\"\/bills\", methods=&#91;\"POST\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">post_bills<\/span><span class=\"hljs-params\">()<\/span> -&gt; Response:<\/span>\n    <span class=\"hljs-keyword\">return<\/span> post(<span class=\"hljs-string\">\"bills\"<\/span>)\n\n\n<span class=\"hljs-meta\">@app.route(\"\/bills\/&lt;int:id&gt;\", methods=&#91;\"PATCH\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">patch_bills<\/span><span class=\"hljs-params\">(id: int)<\/span> -&gt; Response:<\/span>\n    <span class=\"hljs-keyword\">return<\/span> patch_one(\n        table_name=<span class=\"hljs-string\">\"bills\"<\/span>,\n        column_name=<span class=\"hljs-string\">\"id\"<\/span>,\n        value=id,\n    )\n\n\n<span class=\"hljs-meta\">@app.route(\"\/bills\/&lt;int:id&gt;\", methods=&#91;\"DELETE\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">delete_bills<\/span><span class=\"hljs-params\">(id: int)<\/span> -&gt; Response:<\/span>\n    <span class=\"hljs-keyword\">return<\/span> delete_one(\n        table_name=<span class=\"hljs-string\">\"bills\"<\/span>,\n        column_name=<span class=\"hljs-string\">\"id\"<\/span>,\n        value=id,\n    )\n\n\n<span class=\"hljs-comment\"># Many to Many relationships:<\/span>\n\n<span class=\"hljs-meta\">@app.route(\"\/people\/&lt;int:people_id&gt;\/events\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_people_events_all<\/span><span class=\"hljs-params\">(people_id: int)<\/span> -&gt; Dict:<\/span>\n    people_events_table = metadata.tables&#91;<span class=\"hljs-string\">\"people_events\"<\/span>]\n    events_table = metadata.tables&#91;<span class=\"hljs-string\">\"events\"<\/span>]\n    result = connection.execute(\n        events_table.select().join(\n            people_events_table,\n            events_table.c.id == people_events_table.c.event_id\n        ).where(\n            people_events_table.c.people_id == people_id\n        )\n    ).fetchall()\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: &#91;\n            dict(event) <span class=\"hljs-keyword\">for<\/span> event <span class=\"hljs-keyword\">in<\/span> result\n        ]\n    }\n\n\n<span class=\"hljs-meta\">@app.route(\"\/people\/&lt;int:people_id&gt;\/events\/&lt;int:event_id&gt;\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_people_events_one<\/span><span class=\"hljs-params\">(people_id: int, event_id: int)<\/span> -&gt; Dict:<\/span>\n    people_events_table = metadata.tables&#91;<span class=\"hljs-string\">\"people_events\"<\/span>]\n    events_table = metadata.tables&#91;<span class=\"hljs-string\">\"events\"<\/span>]\n    result = connection.execute(\n        events_table.select().join(\n            people_events_table,\n            events_table.c.id == people_events_table.c.event_id\n        ).where(\n            (people_events_table.c.people_id == people_id)\n            &amp; (people_events_table.c.event_id == event_id)\n        )\n    ).fetchall()\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: &#91;\n            dict(event) <span class=\"hljs-keyword\">for<\/span> event <span class=\"hljs-keyword\">in<\/span> result\n        ]\n    }\n\n\n<span class=\"hljs-meta\">@app.route(\"\/events\/&lt;int:event_id&gt;\/people\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_events_people_all<\/span><span class=\"hljs-params\">(event_id: int)<\/span> -&gt; Dict:<\/span>\n    people_events_table = metadata.tables&#91;<span class=\"hljs-string\">\"people_events\"<\/span>]\n    people_table = metadata.tables&#91;<span class=\"hljs-string\">\"people\"<\/span>]\n    result = connection.execute(\n        people_table.select().join(\n            people_events_table,\n            people_table.c.id == people_events_table.c.event_id\n        ).where(\n            people_events_table.c.event_id == event_id\n        )\n    ).fetchall()\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: &#91;\n            dict(event) <span class=\"hljs-keyword\">for<\/span> event <span class=\"hljs-keyword\">in<\/span> result\n        ]\n    }\n\n\n<span class=\"hljs-meta\">@app.route(\"\/events\/&lt;int:event_id&gt;\/people\/&lt;int:people_id&gt;\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_events_people_one<\/span><span class=\"hljs-params\">(event_id: int, people_id: int)<\/span> -&gt; Dict:<\/span>\n    people_events_table = metadata.tables&#91;<span class=\"hljs-string\">\"people_events\"<\/span>]\n    people_table = metadata.tables&#91;<span class=\"hljs-string\">\"people\"<\/span>]\n    result = connection.execute(\n        people_table.select().join(\n            people_events_table,\n            people_table.c.id == people_events_table.c.event_id\n        ).where(\n            (people_events_table.c.people_id == people_id)\n            &amp; (people_events_table.c.event_id == event_id)\n        )\n    ).fetchall()\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: &#91;\n            dict(event) <span class=\"hljs-keyword\">for<\/span> event <span class=\"hljs-keyword\">in<\/span> result\n        ]\n    }\n\n\n<span class=\"hljs-meta\">@app.route(\"\/people\/&lt;int:people_id&gt;\/events\/&lt;int:event_id&gt;\", methods=&#91;\"PATCH\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">patch_people_events<\/span><span class=\"hljs-params\">(people_id: int, event_id: int)<\/span> -&gt; Response:<\/span>\n    <span class=\"hljs-comment\"># Ideally there'd be error handling for if the event doesn't exist here<\/span>\n\n    <span class=\"hljs-comment\"># Sign person up for event<\/span>\n    signup_user(people_id, event_id)\n    <span class=\"hljs-keyword\">return<\/span> Response(status=<span class=\"hljs-number\">204<\/span>)  <span class=\"hljs-comment\"># No Content<\/span>\n\n\n<span class=\"hljs-meta\">@app.route(\"\/events\/&lt;int:event_id&gt;\/people\/&lt;int:people_id&gt;\", methods=&#91;\"PATCH\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">patch_events_people<\/span><span class=\"hljs-params\">(event_id: int, people_id: int)<\/span> -&gt; Response:<\/span>\n    <span class=\"hljs-comment\"># Ideally there'd be error handling for if the event doesn't exist here<\/span>\n\n    <span class=\"hljs-comment\"># Sign person up for event<\/span>\n    signup_user(people_id, event_id)\n    <span class=\"hljs-keyword\">return<\/span> Response(status=<span class=\"hljs-number\">204<\/span>)  <span class=\"hljs-comment\"># No Content<\/span>\n\n\n<span class=\"hljs-meta\">@app.route(\"\/people\/&lt;int:people_id&gt;\/events\/&lt;int:event_id&gt;\", methods=&#91;\"DELETE\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">delete_people_events<\/span><span class=\"hljs-params\">(people_id: int, event_id: int)<\/span> -&gt; Response:<\/span>\n    delete_signup(people_id, event_id)\n    <span class=\"hljs-keyword\">return<\/span> Response(status=<span class=\"hljs-number\">204<\/span>)  <span class=\"hljs-comment\"># No Content<\/span>\n\n\n<span class=\"hljs-meta\">@app.route(\"\/events\/&lt;int:event_id&gt;\/people\/&lt;int:people_id&gt;\", methods=&#91;\"DELETE\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">delete_events_people<\/span><span class=\"hljs-params\">(event_id: int, people_id: int)<\/span> -&gt; Response:<\/span>\n    delete_signup(people_id, event_id)\n    <span class=\"hljs-keyword\">return<\/span> Response(status=<span class=\"hljs-number\">204<\/span>)  <span class=\"hljs-comment\"># No Content<\/span>\n\n\n<span class=\"hljs-comment\"># 1 to Many Relationships:<\/span>\n\n<span class=\"hljs-meta\">@app.route(\"\/people\/&lt;int:people_id&gt;\/bills\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_people_bills_all<\/span><span class=\"hljs-params\">(people_id: int)<\/span> -&gt; Dict:<\/span>\n    bills_table = metadata.tables&#91;<span class=\"hljs-string\">\"bills\"<\/span>]\n    people_table = metadata.tables&#91;<span class=\"hljs-string\">\"people\"<\/span>]\n    result = connection.execute(\n        bills_table.select().join(\n            people_table,\n            people_table.c.id == bills_table.c.payer_id\n        ).where(\n            (people_table.c.id == people_id)\n        )\n    ).fetchall()\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: &#91;\n            dict(event) <span class=\"hljs-keyword\">for<\/span> event <span class=\"hljs-keyword\">in<\/span> result\n        ]\n    }\n\n\n<span class=\"hljs-meta\">@app.route(\"\/people\/&lt;int:people_id&gt;\/bills\/&lt;int:bill_id&gt;\", methods=&#91;\"GET\"])<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_people_bills_one<\/span><span class=\"hljs-params\">(people_id: int, bill_id: int)<\/span> -&gt; Dict:<\/span>\n    bills_table = metadata.tables&#91;<span class=\"hljs-string\">\"bills\"<\/span>]\n    people_table = metadata.tables&#91;<span class=\"hljs-string\">\"people\"<\/span>]\n    result = connection.execute(\n        bills_table.select().join(\n            people_table,\n            people_table.c.id == bills_table.c.payer_id\n        ).where(\n            (bills_table.c.id == bill_id)\n            &amp; (people_table.c.id == people_id)\n        )\n    ).fetchall()\n    <span class=\"hljs-keyword\">return<\/span> {\n        <span class=\"hljs-string\">\"result\"<\/span>: &#91;\n            dict(event) <span class=\"hljs-keyword\">for<\/span> event <span class=\"hljs-keyword\">in<\/span> result\n        ]\n    }\n\n\n<span class=\"hljs-keyword\">if<\/span> __name__ == <span class=\"hljs-string\">\"__main__\"<\/span>:\n    app.run()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This gives the user full CRUD functionality over all pieces of data and their relationships.<\/p>\n\n\n\n<p>We could always make the many to many relationships nicer to use for a human (by doing things such as returning all pieces of data in one endpoint), and there are styles of APIs that do this, but it\u2019s worth remembering that APIs are meant for machines, so the easiest way to programmatically use an API is the priority when designing them.<\/p>\n\n\n\n<p>In the end, what all of this means is that we use relationships in SQL to minimize data duplication, and we use the four REST verbs in an API to allow code written by others to easily interact with our data.<\/p>\n\n\n\n<p>The implementation of said API involves things like inline <code>for<\/code> loops to build lists which allows for shorter, easier to read code. This concept is called &#8220;list comprehension&#8221;, and if you&#8217;d like to read more about it, <a href=\"https:\/\/coderpad.io\/blog\/development\/python-list-comprehension-guide\/\">CoderPad has a guide on how to write them yourself.<\/a><\/p>\n\n\n\n<p>Similarly, if you&#8217;re looking to expand your knowledge of Python&#8217;s internals, maybe take a look at <a href=\"https:\/\/coderpad.io\/blog\/development\/guide-to-python-magic-methods\/\">CoderPad&#8217;s guide to Python&#8217;s Magic Methods<\/a>, which can be used for things like getting attributes of data.<\/p>\n\n\n\n<p>These concepts can also be applied to other types of databases, so if you\u2019re interested it\u2019d be worth it to check out <a href=\"https:\/\/coderpad.io\/blog\/development\/sql-vs-nosql-with-james-quick\/\">CoderPad\u2019s guide to SQL versus NoSQL<\/a>.<\/p>\n\n\n\n<p><em><a href=\"https:\/\/www.linkedin.com\/in\/jennifer-vannier\/\" target=\"_blank\" rel=\"noreferrer noopener\">Jennifer<\/a> is a full stack developer with a passion for all areas of software development. He loves being a polyglot of programming languages and teaching others what he&#8217;s learned.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The backbone of back-end programming is the act of sending, receiving, and manipulating data. The two most common ways of interacting with backend data are SQL (&#8220;Structured Query Language&#8221;) and REST (&#8220;REpresentational State Transfer&#8221;) APIs. SQL is the method that many servers use to store their data, while REST is a method of transferring that [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":9103,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[9],"tags":[],"persona":[29],"blog-programming-language":[66],"keyword-cluster":[],"class_list":["post-9050","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development"],"acf":[],"_links":{"self":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts\/9050","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/comments?post=9050"}],"version-history":[{"count":49,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts\/9050\/revisions"}],"predecessor-version":[{"id":34592,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts\/9050\/revisions\/34592"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/media\/9103"}],"wp:attachment":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/media?parent=9050"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/categories?post=9050"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/tags?post=9050"},{"taxonomy":"persona","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/persona?post=9050"},{"taxonomy":"blog-programming-language","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/blog-programming-language?post=9050"},{"taxonomy":"keyword-cluster","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/keyword-cluster?post=9050"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}