{"id":15107657,"url":"https://github.com/astares/tealight","last_synced_at":"2025-10-23T02:31:23.176Z","repository":{"id":46721837,"uuid":"66116519","full_name":"astares/Tealight","owner":"astares","description":"Minimalistic framework on top of Teapot to easily provide REST Services","archived":false,"fork":false,"pushed_at":"2025-01-04T23:50:58.000Z","size":501,"stargazers_count":23,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-30T17:11:25.086Z","etag":null,"topics":["pharo","rest-api","web-development"],"latest_commit_sha":null,"homepage":"","language":"Smalltalk","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/astares.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-08-19T22:39:41.000Z","updated_at":"2025-01-04T23:51:01.000Z","dependencies_parsed_at":"2024-03-25T02:29:50.606Z","dependency_job_id":"108ee84b-a5c4-472b-8086-9f519f5fe828","html_url":"https://github.com/astares/Tealight","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astares%2FTealight","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astares%2FTealight/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astares%2FTealight/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astares%2FTealight/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/astares","download_url":"https://codeload.github.com/astares/Tealight/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237769067,"owners_count":19363250,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pharo","rest-api","web-development"],"created_at":"2024-09-25T21:40:47.759Z","updated_at":"2025-10-23T02:31:22.726Z","avatar_url":"https://github.com/astares.png","language":"Smalltalk","readme":"# Tealight\n\n**Tealight** is a web and REST framework for [Pharo](https://www.pharo.org) written by T. Bergmann (astares)\n\nIt is a project defining a few extensions to the **[Teapot](https://github.com/zeroflag/Teapot) web framework** to make the (tea) time you spend with the [Pharo](http://www.pharo.org) Teapot system even easier. \n\n[![Pharo](https://img.shields.io/static/v1?style=for-the-badge\u0026message=Pharo\u0026color=3297d4\u0026logo=Harbor\u0026logoColor=FFFFFF\u0026label=)](https://www.pharo.org) \n\n[![Build](https://github.com/astares/Tealight/actions/workflows/build.yml/badge.svg)](https://github.com/astares/Tealight/actions/workflows/build.yml)\n[![Coverage Status](https://codecov.io/github/astares/Tealight/coverage.svg?branch=main)](https://codecov.io/gh/astares/Tealight/branch/main)\n\n[![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n[![Pharo 7](https://img.shields.io/badge/Pharo-7.0-%23aac9ff.svg)](https://pharo.org/download)\n[![Pharo 8](https://img.shields.io/badge/Pharo-8.0-%23aac9ff.svg)](https://pharo.org/download)\n[![Pharo 9](https://img.shields.io/badge/Pharo-9.0-%23aac9ff.svg)](https://pharo.org/download)\n[![Pharo 10](https://img.shields.io/badge/Pharo-10-%23aac9ff.svg)](https://pharo.org/download)\n[![Pharo 11](https://img.shields.io/badge/Pharo-11-%23aac9ff.svg)](https://pharo.org/download)\n[![Pharo 12](https://img.shields.io/badge/Pharo-12-%23aac9ff.svg)](https://pharo.org/download)\n\nInstall\n---------\nYou can install **Tealight** by executing the following load scripts:\n\n```Smalltalk\nMetacello new \n\trepository: 'github://astares/Tealight:main/src';\n\tbaseline: 'Tealight';\n\tload \t\n```\t\n\t\nAlternatively you can install **Tealight** also from the [Pharo catalog](http://catalog.pharo.org) using the *Catalog Browser* right from within your Pharo image.\n\nBasic Usage\n---------\n\n### Working with the Tealight server\nAfter you have the framework installed you can easily start a **Tealight** web server by selecting\n\n _\"Tealight\"_ -\u003e _\"WebServer\"_ -\u003e _\"Start webserver\"_\n\nfrom the Pharo world menu. Internally this starts a Teapot server with some defaults.\n\n![Tealight menu](images/Menu.png)\n\nYou can also easily stop the server from the Tealight web server menu by using _\"Stop webserver\"_ or open a webbrowser on the server by using _\"Browse\"_.\n\n### Accessing the default Teapot\nAfter you started the server you can easily access the running Teapot instance from your code or playground\n\n```Smalltalk\nTLWebserver teapot\n```\n\nYou can easily experiment with Teapot routes, for instance using\n\n```Smalltalk\nTLWebserver teapot \n\tGET: '/hi' -\u003e 'HelloWorld'\n```\n\nIf you point your browser to [http://localhost:8080/hi](http://localhost:8080/hi) you will receive your first \"HelloWorld\" greeting.\n\nIf you open an inspector on the Teapot instance \n\n```Smalltalk\nTLWebserver teapot inspect\n```\n\nyou will see that a dynamic route was added:\n\n![Inspector on the teapot](images/InspectorTeapot.png)\n\nSo you can dynamically add new routes for GET, POST, DELETE or other HTTP methods interactively.\n\nWe recommend to read the [Teapot chapter of the Pharo Enterprise Book]([https://ci.inria.fr/pharo-contribution/job/EnterprisePharoBook/lastSuccessfulBuild/artifact/book-result/Teapot/Teapot.html](http://files.pharo.org/books-pdfs/entreprise-pharo/2016-10-06-EnterprisePharo.pdf)) to get a better understanding of the possibilities of the underlying **Teapot** framework.\n\nExperimenting\n---------\n\n### Experimenting with Dynamic Routes\n\nLets add another dynamic route on our default Teapot, this time a dynamic route with a block as an action.\nThe block itself receives the request as an argument:\n\n```Smalltalk\nTLWebserver teapot \n\tGET: '/hello/\u003cuser\u003e' -\u003e [:req | 'Hello ', (req at: #user)]\n```\n\nYou can now use your web browser again to call the newly defined URL. This time you need to give a parameter when you point the webbrowser to the new URL. \n\n[http://localhost:8080/hello/Pharo](http://localhost:8080/hello/Pharo)  returns **Hello Pharo** while\n\n[http://localhost:8080/hello/Smalltalk](http://localhost:8080/hello/Smalltalk)  returns **Hello Smalltalk** \n\n### Introduction to Teaspoon\n\nThere is a new tool called **Teaspoon** that was added as a custom inspector tool on dynamic routes by Attila Magyar. Attila is the author of Teapot. Let's see how we can access this nice tool.\n\nFirst inspect our Teapot instance again \n\n```Smalltalk\nTLWebserver teapot inspect\n```\n\nbut this time click on the dynamic route that we added lately in the displayed list of dynamic routes. The inspector will navigate into another inspector displaying the dynamic route instance. There is a second custom tab extension in this inspector called \"Teaspoon\":\n\n![Inspector on the teapot](images/Teaspoon.png)\n\nIn this inspector tool you see the HTTP method (here **GET**) and the full URL of your dynamic route. You can change the URL by replacing _\u003cuser\u003e_ with your name and click on _\"Execute Request\"_.\n\nThis will perform the request and display the result in the inspector again. So you can interactively test your routes right from within your Pharo image without switching to external tools.\n\nThe **Teaspoon** tool is especially useful when you experiment with route patterns or if you want to define, implement and test a REST based API with Pharo.\n\n### Cleaning up\n\nIf you (by accident) messed up with a single route defintion during your experiments the context menu in the inspector comes to the rescue:\n\n\n![Remove dynamic routes](images/ContextMenu.png)\n\n\n\nto delete a single route or all routes. You can even clean up via script:\n\n```Smalltalk\nTLWebserver teapot \n\tremoveAllDynamicRoutes \n```\n\nFor the next part of this tutorial lets remove all defined routes.\n\nDefining a REST based interface\n---------\n\n### REST API in annotated methods\n\nWhile it is nice to experiment with dynamic routes by adding them one by one to the Teapot instance it would be even more convinient \n\n * if we could define the REST API using regular Smalltalk methods\n * if we could map each URL easily\n\nTo support that ***Tealight*** adds a special utility class (called _TLRESTAPIBuilder_) to help you easily build an API. Lets see how we can use it.\n\nFirst of all we need to create a simple class in the system either from the browser or with an expression:\n\n```Smalltalk\nObject subclass: #ExampleRESTAPI\n\tinstanceVariableNames: ''\n\tclassVariableNames: ''\n\tpackage: 'MyApp-REST-API'\n```\t\n\nNow we can define a class side method:\n\n```Smalltalk\ngreeting: aRequest\n\t\u003cREST_API: 'GET' pattern: 'hello'\u003e\n\t\n\t^'HelloWorld from Pharo'\n```\t\n\nAs you see we use a pragma in this class marking the class side method as REST API method and defining the kind of HTTP method we support as well as the function path for our little REST service.\n\n### Generate our web API\n\nNow we can use the utility class to generate the dynamic routes for us sending a message to our class ending up in our method:\n\n```Smalltalk\nTLRESTAPIBuilder buildAPI \n```\t\n\nThis creates the dynamic routes for us:\n\n\n![A dynamic route defined from a method](images/RESTRoute.png)\n\n\nAlso that by default there is an ***\"api\"*** prefix generated into the URL for all REST based methods so you\nneed to point your browser to:\n\n  [http://localhost:8080/api/hello]()\n\nIf you dislike the default API prefix you can customize this for your own needs:\n\n```Smalltalk\nTLRESTApiURLPathBuilder withoutPrefix   \"have no API prefix in the URL\" \n```\t\n\nwhich would result in [http://localhost:8080/hello]() or \n\n```Smalltalk\nTLRESTApiURLPathBuilder useAPIPrefix: 'mykillerapp/myapi'   \"have a custom API prefixin the URL\" \n```\t\n\nto use a custom prefix for the API location [http://localhost:8080/mykillerapp/myapi/hello]()\n\nAdvanced usage\n---------\n\n### Versioned REST APIs\n\nThere are different architectural styles to define a web based API. As with any regular API you might want to version your API and use a specific part of the URL to depict the version that is used.\n\nIf you want to do this you can use another pragma to \n\n```Smalltalk\ncallMe: aRequest\n\t\u003cREST_API: 'GET' versions: #('v1') pattern: 'hello'\u003e\n\t\n\tself halt\n```\t\n\nNow rebuild the whole API quickly by cleaning up and regenerating the dynamic routes again (using the default prefix):\t\n\t\n```Smalltalk\t\nTLRESTApiURLPathBuilder useStandardPrefix. \nTLWebserver teapot removeAllDynamicRoutes.\nTLRESTAPIBuilder buildAPI \t\n```\t\n\nThis will now generate a [http://localhost:8080/api/v1/hello]() route - again with a call that ends up in our own method. The **v1** part in the URL shows the user of the API that this is for version 1 of the interface.\n\n### Supporting more than one version\n\nAs you may have guessed already you can give more than one version in the pragma. A function that\nis supported in two version can be annontated like this:\n\n```Smalltalk\t\ncallAnother: aRequest\n\t\u003cREST_API: 'GET' versions: #('v1' 'v2') pattern: 'hello'\u003e\n\t\n\t^'A method that is supported in both versions'\n```\n\nwill create two dynamic routes:\n\n * [http://localhost:8080/api/v1/hello]()\n * [http://localhost:8080/api/v2/hello]()\n\n### Advanced Usage\n\nOver time the API that you provide in your webapp might change from one version of the web interface to another. \n\nFor instance a web method might need to be renamed to fix a typo. This can easily be done now:\n\n```Smalltalk\t\ncallRenamed: aRequest\n    \u003cREST_API: 'GET' versions: #('v1') pattern: 'oldName'\u003e\n\t \u003cREST_API: 'GET' versions: #('v2') pattern: 'newName'\u003e\n\n    ^'A method that was renamed between two API versions'\n```\n\nwill create two dynamic routes:\n\n * [http://localhost:8080/api/v1/oldName]()\n * [http://localhost:8080/api/v2/newName]()\n\nSo in version **v1** you still support the old name but the users of your API are encouraged to use the new name in version **v2**.\n  \nIt might also be necessary to move the location of a method in a higher version of your interface - but\nyou still might want to stay compatible to the old version: \n\n```Smalltalk\t\ncallMoved: aRequest\n    \u003cREST_API: 'GET' versions: #('v1') pattern: 'firstAppearance'\u003e\n\t \u003cREST_API: 'GET' versions: #('v2') pattern: 'moved/newAppearance'\u003e\n\n    ^'A method that was moved between two API versions' \n```    \n\nwill create two dynamic routes:\n\n * [http://localhost:8080/api/v1/firstAppearance]()\n * [http://localhost:8080/api/v2/moved/newAppearance]()\n\nSo the old route could be used as well as the new one - but still a single Pharo method is called.\n\n\n### Summary\n\nTealight makes it easy to experiment with Teapot framework and allows you to easily generate a web based API. While we demonstrated most code here with a HTTP GET all the examples could be done with PUT, DELETE, POST, ... as well.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fastares%2Ftealight","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fastares%2Ftealight","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fastares%2Ftealight/lists"}