{"id":13752872,"url":"https://github.com/noboomu/proteus","last_synced_at":"2025-05-16T15:03:37.360Z","repository":{"id":43422311,"uuid":"89373265","full_name":"noboomu/proteus","owner":"noboomu","description":"Proteus is a blazing fast minimalist Java web server framework built atop Undertow.","archived":false,"fork":false,"pushed_at":"2025-04-29T00:34:23.000Z","size":27455,"stargazers_count":189,"open_issues_count":1,"forks_count":18,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-05-16T15:03:26.222Z","etag":null,"topics":["api","api-server","high-performance","http2","java","jax-rs","microservice","microservice-framework","mvc","openapi","reactive","rest","rest-api","restful-api","swagger","undertow","web-framework"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/noboomu.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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,"zenodo":null}},"created_at":"2017-04-25T14:55:16.000Z","updated_at":"2025-04-29T00:34:26.000Z","dependencies_parsed_at":"2022-07-11T03:47:31.720Z","dependency_job_id":"efa6468b-79f2-4e28-88c5-cdcae6150a37","html_url":"https://github.com/noboomu/proteus","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noboomu%2Fproteus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noboomu%2Fproteus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noboomu%2Fproteus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noboomu%2Fproteus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/noboomu","download_url":"https://codeload.github.com/noboomu/proteus/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254553937,"owners_count":22090416,"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":["api","api-server","high-performance","http2","java","jax-rs","microservice","microservice-framework","mvc","openapi","reactive","rest","rest-api","restful-api","swagger","undertow","web-framework"],"created_at":"2024-08-03T09:01:12.004Z","updated_at":"2025-05-16T15:03:37.308Z","avatar_url":"https://github.com/noboomu.png","language":"Java","readme":"\n![Alt logo](https://raw.githubusercontent.com/noboomu/proteus/master/proteus-core/src/main/resources/proteus-logo.svg?sanitize=true)\n\nProteus is a __blazing fast minimalist__ Java API server framework built atop [Undertow](http://undertow.io) for developing back end applications and microservices.\n\n- __NO MAGIC__\n- Incredibly easy to use and get started\n- Limited dependencies and \u003c 340kb\n- JAX-RS compliant\n- Easy on the developer and the metal\n- Blazing fast!!!\n[The latest Techempower benchmarks](https://www.techempower.com/benchmarks/) demonstrate __proteus__ outperforming the top Java web frameworks\n\n![Top in Java Frameworks for JSON](https://github.com/noboomu/proteus-example/blob/master/src/main/resources/images/benchmark1.png?raw=true)\n\n![Top in Java Frameworks for Plaintext](https://github.com/noboomu/proteus-example/blob/master/src/main/resources/images/benchmark2.png?raw=true)\n\nTL;DR\n---------------\n- Proteus rewrites your controller methods into high performance Undertow response handlers at run time.\n- The goal is to provide the absolute highest performance while providing a simple and familiar API. \n- As developers, we feel a web framework should provide the essentials at minimal cost. \n\nGetting Started\n---------------\n\n### Quick Start\n- Make sure you have a JDK \u003e= 8 and a current version of Maven installed.\n- Copy and paste into your terminal:\n```\n/bin/bash -e \u003c(curl -fsSL https://raw.githubusercontent.com/noboomu/proteus-example/master/scripts/quickStart.sh)\n```\n\n- Open [http://localhost:8090/v1/openapi](http://localhost:8090/v1/openapi) for a v3 OpenAPI UI.\n\n### As a dependency\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.sinistral\u003c/groupId\u003e\n    \u003cartifactId\u003eproteus-core\u003c/artifactId\u003e\n    \u003cversion\u003e0.6.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nOpenAPI v3 Support\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.sinistral\u003c/groupId\u003e\n    \u003cartifactId\u003eproteus-openapi\u003c/artifactId\u003e\n    \u003cversion\u003e0.6.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nControllers\n---------------\n\n### Supported Controller Annotations\n\nController classes respect standard JAX-RS annotations:\n```java\n@Path(\"/benchmarks\")\n@Produces((MediaType.APPLICATION_JSON)) \n@Consumes((MediaType.MEDIA_TYPE_WILDCARD))\npublic class DemoController\n```\n\n### Supported Method Annotations\n\nController class methods respect standard Swagger / JAX-RS annotations:\n```java\n\n\n@GET\n@Path(\"/plaintext\")\n@Produces((MediaType.TEXT_PLAIN))\npublic ServerResponse\u003cByteBuffer\u003e plaintext(ServerRequest request)\n{ \n\treturn response(\"Hello, World!\").textPlain();\n}\n```\n\n\u003e OpenAPI v3 annotations are supported when using the `proteus-openapi` module.\n\nProteus has three built in annotations:\n\n* @Blocking\n    * ```io.sinistral.proteus.annotations.Blocking```\n    * Forces the request processing to block.\n\n* @Debug\n    * ```io.sinistral.proteus.annotations.Debug```\n    * Dumps the request and response details to the log.\n\n* @Chain\n    * ```io.sinistral.proteus.annotations.Chain```\n    * Wraps the endpoint handler in the provided array of ```io.undertow.server.HttpHandler``` classes.\n\nController methods arguments support the following [JAX-RS annotations](https://docs.oracle.com/javaee/7/api/index.html?javax/ws/rs/PathParam.html):\n\n* @PathParam\n    * ```javax.ws.rs.PathParam```\n    * Binds a url template parameter to the method parameter.\n    * i.e. if the path is `/dogs/{id}`, @PathParam(\"id\") binds the path segment value to the method parameter.\n\n* @QueryParam\n    * ```javax.ws.rs.QueryParam```\n    * Binds a HTTP query parameter to the method parameter.\n\n* @FormParam\n    * ```javax.ws.rs.FormParam```\n    * Binds the form parameter within a request body to the method parameter.\n\n* @HeaderParam\n    * ```javax.ws.rs.HeaderParam```\n    * Binds the value of a HTTP header to the method parameter.\n    \n* @CookieParam\n    * ```javax.ws.rs.CookieParam```\n    * Binds the value of a HTTP cookie to the method parameter.\n    \n * @BeanParam\n    * ```javax.ws.rs.BeanParam```\n    * Binds and attempts to convert the request body to an instance of the method parameter.\n\n* @DefaultParam\n    * ```javax.ws.rs.DefaultParam```\n    * Sets the default value of a method parameter.\n    \n## Methods and Return Types\n\n###### *The examples below assume you've included the `proteus-openapi` module.\n\n#### Performance\nFor total control and maximum performance the raw `HttpServerExchange` can be passed to the controller method.\n\nMethods that take an `HttpServerExchange` as an argument should __not__ return a value.\n\nIn this case the method takes on __full responsibility__ for completing the exchange.\n \n#### Convenience\n\n\nThe static method ```io.sinistral.proteus.server.ServerResponse.response``` helps create ```ServerResponse\u003cT\u003e``` instances that are the preferred return type for controller methods.\n\nIf the response object's `contentType` is not explicitly set, the `@Produces` annotation is used in combination with the `Accept` headers to determine the `Content-Type`.\n\nFor methods that should return a `String` or `ByteBuffer` to the client users can create responses like this:\n ```java\n  ...\n  import static io.sinistral.proteus.server.ServerResponse.response;\n  ...\n@GET\n@Path(\"/hello-world\")\n@Produces((MediaType.TEXT_PLAIN)) \n@Operation(description = \"Serve a plaintext message using a ServerResponse\")\npublic ServerResponse\u003cByteBuffer\u003e plaintext(ServerRequest request, @QueryParam(\"message\") String message)\n{ \n\treturn response(\"Hello, World!\").textPlain();\n}\n```\nBy default, passing a `String` to the static `ServerResponse.response` helper function will convert it into a `ByteBuffer`.\n\nFor other types of responses the following demonstrates the preferred style:\n```java\n  ...\n \n  import static io.sinistral.proteus.server.ServerResponse.response;\n   ...\n@GET\n@Path(\"/world\")\n@Produces((MediaType.APPLICATION_JSON)) \n@Operation(description = \"Return a world JSON object\")\npublic ServerResponse\u003cWorld\u003e getWorld(ServerRequest request, @QueryParam(\"id\") Integer id,  @QueryParam(\"randomNumber\") Integer randomNumber )\n{ \n\treturn response(new World(id,randomNumber)).applicationJson();\n}\n```\nThe entity can be set separately as well:\n\u003e this disables static type checking!\n\n```java\n  ...\n \n  import static io.sinistral.proteus.server.ServerResponse.response;\n  ...\n@GET\n@Path(\"/world\")\n@Produces((MediaType.APPLICATION_JSON)) \n@Operation(description = \"Return a world JSON object\")\npublic ServerResponse getWorld(ServerRequest request, Integer id,  Integer randomNumber )\n{ \n\treturn response().entity(new World(id,randomNumber));\n}\n\n```\n`CompletableFuture\u003cServerResponse\u003cT\u003e\u003e` can also be used as a response type:\n\n```java\n  ...  \n  import static io.sinistral.proteus.server.ServerResponse.response; \n  ...\n@GET\n@Path(\"/future/user\")\n@Operation(description = \"Future user endpoint\"  )\npublic CompletableFuture\u003cServerResponse\u003cUser\u003e\u003e futureUser( ServerRequest request )\n{ \n\treturn CompletableFuture.completedFuture(response( new User(123L) ).applicationJson() );\n}\n```\n\nIn this case a handler will be generated with the following source code:\n\n```java\n  ...\n  import static io.sinistral.proteus.server.ServerResponse.response;\n  ...\npublic void handleRequest(final io.undertow.server.HttpServerExchange exchange) throws java.lang.Exception { \n    CompletableFuture\u003cServerResponse\u003cUser\u003e\u003e response = examplesController.futureUser();\n    response.thenAccept( r -\u003e  r.applicationJson().send(this,exchange) )\n    \t.exceptionally( ex -\u003e  {\n      \t\tthrow new java.util.concurrent.CompletionException(ex);\n  \t} );\n}\n```\n\nController Parameters\n--------------\n\nA ```io.sinistral.proteus.server.ServerRequest``` can be added as an endpoint parameter if the user wishes to access request properties that are not included in the parameter list.\n\nProteus is capable of parsing most types of endpoint parameters automatically so long as the type has a ```fromString```, ```valueOf```, or can be deserialized from JSON.\n\nMultipart/Form file uploads can be passed to the endpoint methods as a ```java.io.File```, a ```java.nio.Files.Path```, or a ```java.nio.ByteBuffer```.\n\nOptional parameters are also supported, here is a more complex endpoint demonstrating several parameter types:\n```java\n  ...\n  import  static  io.sinistral.proteus.server.ServerResponse.response;\n  ...\n@GET\n@Path(\"/response/parameters/complex/{pathLong}\")\n@Operation(description = \"Complex parameters\")\npublic ServerResponse\u003cMap\u003cString,Object\u003e\u003e complexParameters(\n        ServerRequest serverRequest, \n        @PathParam(\"pathLong\") final Long pathLong, \n        @QueryParam(\"optionalQueryString\") Optional\u003cString\u003e optionalQueryString, \n        @QueryParam(\"optionalQueryLong\") Optional\u003cLong\u003e optionalQueryLong, \n        @QueryParam(\"optionalQueryDate\") Optional\u003cOffsetDateTime\u003e optionalQueryDate, \n        @QueryParam(\"optionalQueryUUID\") Optional\u003cUUID\u003e optionalQueryUUID, \n        @HeaderParam(\"optionalHeaderUUID\") Optional\u003cUUID\u003e optionalHeaderUUID,\n        @QueryParam(\"optionalQueryEnum\") Optional\u003cUser.UserType\u003e optionalQueryEnum,\n        @HeaderParam(\"optionalHeaderString\") Optional\u003cString\u003e optionalHeaderString,\n        @QueryParam(\"queryUUID\") UUID queryUUID,  \n        @HeaderParam(\"headerString\") String headerString,\n        @QueryParam(\"queryEnum\") User.UserType queryEnum, \n\t    @QueryParam(\"queryIntegerList\") List\u003cInteger\u003e queryIntegerList, \n\t    @QueryParam(\"queryLong\") Long queryLong \n\t    )\n\t{\n\t\tMap\u003cString,Object\u003e responseMap = new HashMap\u003c\u003e(); \n\t\tresponseMap.put(\"optionalQueryString\", optionalQueryString.orElse(null));\n\t\tresponseMap.put(\"optionalQueryLong\", optionalQueryLong.orElse(null));\n\t \tresponseMap.put(\"optionalQueryDate\", optionalQueryDate.map(OffsetDateTime::toString).orElse(null));\n\t\tresponseMap.put(\"optionalQueryUUID\", optionalQueryUUID.map(UUID::toString).orElse(null));\n\t\tresponseMap.put(\"optionalHeaderUUID\", optionalHeaderUUID.map(UUID::toString).orElse(null));\n\t\tresponseMap.put(\"optionalHeaderString\", optionalHeaderString.orElse(null));\n\t\tresponseMap.put(\"optionalQueryEnum\", optionalQueryEnum.orElse(null));\n\t\tresponseMap.put(\"queryEnum\", queryEnum);\n\t\tresponseMap.put(\"queryUUID\", queryUUID.toString()); \n\t\tresponseMap.put(\"queryLong\", queryLong);\n\t\tresponseMap.put(\"pathLong\", pathLong);\n\t\tresponseMap.put(\"headerString\", headerString); \n\t\tresponseMap.put(\"queryIntegerList\", queryIntegerList); \n\t\treturn response(responseMap).applicationJson(); \n\t}\n```\n\n\nServices\n-------------\n\nProteus has two standard services that extend the ```io.sinistral.proteus.services.DefaultService``` class.\nThe ```io.sinistral.proteus.services.DefaultService``` extends ```com.google.common.util.concurrent.AbstractIdleService``` and implements the ```io.sinistral.proteus.services.BaseService``` interface.\nThe ProteusApplication class expects services that implement ```io.sinistral.proteus.services.BaseService```.\n\n- __AssetsService__\n\n    Included in the `proteus-core` module.\n\n\tThe AssetsService mounts an asset directory at a given path and is configured in your ```application.conf``` file.\n\n\tThe default configuration:\n\t```\n\tassets {\n \t   # the base path assets will be server from\n  \t  path = \"/public\"\n  \t  # the directory to load the assets from\n   \t dir = \"./assets\"\n   \t cache {\n   \t # cache timeout for the assets\n   \t     time = 500\n  \t  }\n\t}\n\t```\n\n \n- __OpenAPIService__   \n\n    Included in the `proteus-openapi` module.\n\n\tThe OpenAPIService generates an openapi-spec file from your endpoints and serves a swagger-ui and spec.\n\t\n\tThe default configuration serves the spec at `{application.path}/openapi.yaml` and the ui at `${application.path}/openapi`.\n\n\tThe service is configured in your ```application.conf``` file.\n\n\tThe default configuration:\n\t```\n\topenapi {\n\n \t basePath= ${application.path}\"/openapi\"\n\n \t port = ${application.ports.http}\n\n \t specFilename=\"openapi.yaml\"\n\n \t openapi=\"3.0.1\"\n\n \t # openapi info\n  info {\n    title = ${application.name}\n    version = ${application.version}\n    description=\"Proteus Server\"\n  }\n  \n  \tsecuritySchemes {\n   \t ApiKeyAuth = {\n    \t type=\"apiKey\"\n     \t in=\"header\"\n     \t name=\"X-API-KEY\" \n   \t }\n  \t}\t\n\n  \tservers = [\n  \t  { \n  \t    url=${application.path}\n   \t   description=\"Default Server\"  \n  \t  }\n \t ]\n\t} \n\t```\n\t\nPlugins / Modules\n-------------\n\n_Where are all of the plugins for everything?!?!_\n\nProteus's design philosophy is a minimal one, so from our perspective managing a long list of plug and play plugins does not make that much sense.\n\nOur experience with other frameworks has been that plugins are swiftly out-dated or hide too much of the underlying implementation to be useful.\n\nHowever, making your own \"plugin\" is simple and much more gratifying.\n\nHere is an example ```AWSModule``` that provides AWS S3 and SES support.\n\nThis example assumes you have defined the relevant aws properties in your config file:\n\n```java\npublic class AWSModule extends AbstractModule\n{ \n\tprivate static Logger log = LoggerFactory.getLogger(AWSModule.class.getCanonicalName());\n\n\t@Inject\n\t@Named(\"aws.accessKey\")\n\tprotected String accessKey;\n\t\n\t@Inject\n\t@Named(\"aws.secretKey\")\n\tprotected String secretKey;\n\n\tpublic void configure()\n\t{\n\n\t\tAWSCredentials credentials = new BasicAWSCredentials(accessKey,  secretKey);\n\t\t\n\t\tAWSStaticCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials);\n\n\t\tbind(AWSStaticCredentialsProvider.class).toInstance(credentialsProvider);\n \n\t\tAmazonS3Client s3Client = (AmazonS3Client) AmazonS3ClientBuilder.standard().withCredentials(credentialsProvider).withRegion(\"us-west-2\").build();\n \t\t\n\t\tTransferManager transferManager = TransferManagerBuilder.standard().withMultipartUploadThreshold(8000000L).withS3Client(s3Client).withExecutorFactory(new ExecutorFactory()\n\t\t{\n\n\t\t\t@Override\n\t\t\tpublic ExecutorService newExecutor()\n\t\t\t{\n\t\t\t\tThreadFactory threadFactory = new ThreadFactory()\n\t\t\t\t{\n\t\t\t\t\tprivate int threadCount = 1;\n\n\t\t\t\t\tpublic Thread newThread(Runnable r)\n\t\t\t\t\t{\n\t\t\t\t\t\tThread thread = new Thread(r);\n\t\t\t\t\t\tthread.setName(\"s3-transfer-manager-worker-\" + threadCount++);\n\t\t\t\t\t\treturn thread;\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\treturn (ThreadPoolExecutor) Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2, threadFactory);\n\t\t\t}\n\n\t\t}).build();\n\t\t \n\t\tAmazonSimpleEmailServiceAsyncClient sesClient = (AmazonSimpleEmailServiceAsyncClient) AmazonSimpleEmailServiceAsyncClientBuilder.standard().withCredentials(credentialsProvider).withRegion(\"us-west-2\").build();\n \n\t\tbind(AmazonSimpleEmailServiceAsyncClient.class).toInstance(sesClient); \n\t\tbind(AmazonS3Client.class).toInstance(s3Client);\n\t\tbind(TransferManager.class).toInstance(transferManager); \n\t}  \n}\n``` \n\nNow you can simply inject these in any controller by adding the following lines to the top of the controller:\n```java\n\t@Inject\n\tprotected TransferManager transferManager;\n\t\n\t@Inject\n\tprotected AmazonS3Client s3Client;\n\t\n\t@Inject\n\tprotected  AmazonSimpleEmailServiceAsyncClient sesClient;\n```\n\nAlso, please note that the implementation of ```ProteusApplication``` you are using would also need to add the following line before starting:\n```java\n\tapp.addModule(AWSModule.class);\n```\n\n\t\nUnder the Hood\n---------------\n\nProteus takes your MVC controller classes and methods decorated with Swagger / JAX-RS annotations and generates native Undertow handler classes at runtime. \n\n\nBy default, the configuration is loaded into a `com.typesafe.config.Config` from a file at `conf/application.conf`.\n\n`@Named` annotated properties of `Module`, `Service` and controller classes are bound to values found in the configuration.\n\nProteus applications generally have a main method that creates an instance of `io.sinistral.proteus.ProteusApplication`. \n\nPrior to calling `start` on the `ProteusApplication` instance:\n* Register `Service` classes via `registerService`\n* Register `Module` classes via `registerModule`\n* Register classes annotated with `javax.ws.rs.Path` via `registerController`\n\nOut of the box you get a [Swagger UI](https://github.com/swagger-api/swagger-ui) at `/openapi`.\n\n\u003e A `Service` extends `com.google.common.util.concurrent.AbstractIdleService` or `io.sinistral.proteus.services.DefaultService`.\n\n\u003e A `Module` implements `com.google.inject.Module`.\n\nExamples\n----------\nCheck out [this example](https://github.com/noboomu/proteus-example) that also demonstrates [pac4j](https://github.com/pac4j/pac4j) integration.\n \n\nMotivation\n----------\n* Several years of working with the [Play](http://playframework.com) framework convinced us there had to be a better way.\n* We faced a Goldilocks Crisis with the existing alternatives: [Jooby](http://jooby.org) did too much, [light-4j](https://github.com/networknt/light-4j) didn't do quite enough.\n* We needed a framework that enabled us to write clean MVC REST controllers that created Swagger docs we could plug directly into the existing [codegen](https://github.com/swagger-api/swagger-codegen) solutions.\n* We needed a framework with minimal overhead and performance at or near that of raw [Undertow](http://undertow.io).\n\n\nInspired by [Play](http://playframework.com), [Jooby](http://jooby.org), and [light-4j](https://github.com/networknt/light-4j).\n\nDependencies\n----------\n* [JDK 17](http://www.oracle.com/technetwork/java/javase/downloads/index.html)\n* [Maven 3](http://maven.apache.org/)\n\n\nBuilt With\n----------\n - [Undertow](http://undertow.io) (server)\n - [Guice](https://github.com/google/guice) (di)\n - [Java Runtime Compiler](https://github.com/OpenHFT/Java-Runtime-Compiler) (runtime generated class compilation)\n - [javapoet](https://github.com/square/javapoet) (runtime class generation)\n - [Jackson](https://github.com/FasterXML/jackson-dataformat-xml) (xml)\n - [jsoniter](http://jsoniter.com/) (json)\n - [Logback](https://logback.qos.ch/) (logging)\n - [Typesafe Config](https://github.com/typesafehub/config) (config)\n - [Swagger](http://swagger.io/) (annotations and swagger spec)\n - [JAX-RS](http://docs.oracle.com/javaee/6/api/javax/ws/rs/package-summary.html) (annotations only)\n\nIn The Wild\n----------\n* [Source One Parts Center](http://s1partscenter.com)\n* [Wurrly](https://wurrly.com)\n\n  \n","funding_links":[],"categories":["开发框架","web-framework"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoboomu%2Fproteus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnoboomu%2Fproteus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoboomu%2Fproteus/lists"}