[{"data":1,"prerenderedAt":1736},["ShallowReactive",2],{"post-\u002Fposts\u002Fhow-to-create-a-websocket-server-in-dotnet":3,"post-author-garywoodfine":1343,"post-aside-video-none":6,"\u002Fposts\u002Fhow-to-create-a-websocket-server-in-dotnet-surround":1352,"navigation":1363,"authors":1411,"author-garywoodfine":1440,"post-aside-related-candidates":1441,"content-section-services":1712,"content-section-products":1715,"content-section-company":1718,"legal-link":1720},{"id":4,"title":5,"aside":6,"author":7,"body":8,"category":1319,"date":1320,"description":1321,"extension":1322,"featureImage":1323,"meta":1325,"navigation":77,"path":1327,"seo":1328,"sitemap":1329,"slug":6,"stem":1339,"subtitle":1340,"summary":1341,"tags":6,"updated":6,"video":6,"__hash__":1342},"posts\u002Fposts\u002Fhow-to-create-a-websocket-server-in-dotnet.md","How to create a WebSocket server in dotnet",null,"garywoodfine",{"type":9,"value":10,"toc":1315},"minimark",[11,24,27,43,48,53,56,59,137,142,153,160,255,270,274,277,280,286,303,310,317,324,327,727,731,734,740,743,756,759,764,969,973,979,985,992,998,1004,1014,1020,1026,1029,1032,1153,1157,1160,1181,1184,1226,1237,1240,1247,1257,1278,1286,1289,1294,1301,1304,1308,1311],[12,13,14,15,23],"p",{},"In ",[16,17,22],"a",{"href":18,"rel":19,"title":21},"https:\u002F\u002Fthreenine.blog\u002Fposts\u002Fwhat-are-web-sockets",[20],"nofollow","What are Web Sockets | threenine.io","What are WebSockets"," we\ncovered the fundamental basics relating to WebSockets and how they can be used to enable Real-Time bidirectional\ncommunication between client and server applications.",[12,25,26],{},"WebSockets provide a number of significant advantages to help implement Real-Time communication in applications, they\ndo require specific server side support and your service application needs to support the WebSocket protocol.",[12,28,29,30,34,35,38,39,42],{},"Dotnet is packaged with a number of improvements and enhancements in the Networking Operations API surface and provides\nimproved capabilities and performance of the existing HTTP and WebSockets protocols. WebSockets are now supported over\nHTTP\u002F2, their handshake response information is exposed in the ",[31,32,33],"code",{},"CollectHttpResponseDetails"," setting of the\n",[31,36,37],{},"ClientWebSocketsOptions"," class and HTTP upgrade requests over WebSockets allow for the passing of a custom ",[31,40,41],{},"HttpClient","\ninstead of the one encapsulated in the request.",[44,45,47],"h3",{"id":46},"how-to-start-a-new-websocket-project","How to start a new Websocket project",[49,50],"github-code",{"account":51,"repo":52},"threenine","tutorials",[12,54,55],{},"In order to demonstrate a very simple websocket application we'll build simple notification application, to send\nmessages.  We'll first start off by creating our websocket server.",[12,57,58],{},"To do this we'll first create a solution file and then create and add a basic Web API project to it.",[60,61,68],"pre",{"className":62,"code":63,"filename":64,"language":65,"meta":66,"style":67},"language-shell shiki shiki-themes material-theme-lighter monokai monokai monokai","\ndotnet new sln --name server\ndotnet new webapi --name server --output src\ndotnet sln Server.sln add src\u002Fserver.csproj\n\n","Terminal","shell","meta-info=val","",[31,69,70,79,100,121],{"__ignoreMap":67},[71,72,75],"span",{"class":73,"line":74},"line",1,[71,76,78],{"emptyLinePlaceholder":77},true,"\n",[71,80,82,86,90,93,97],{"class":73,"line":81},2,[71,83,85],{"class":84},"sC-bC","dotnet",[71,87,89],{"class":88},"sKdu0"," new",[71,91,92],{"class":88}," sln",[71,94,96],{"class":95},"sbSe9"," --name",[71,98,99],{"class":88}," server\n",[71,101,103,105,107,110,112,115,118],{"class":73,"line":102},3,[71,104,85],{"class":84},[71,106,89],{"class":88},[71,108,109],{"class":88}," webapi",[71,111,96],{"class":95},[71,113,114],{"class":88}," server",[71,116,117],{"class":95}," --output",[71,119,120],{"class":88}," src\n",[71,122,124,126,128,131,134],{"class":73,"line":123},4,[71,125,85],{"class":84},[71,127,92],{"class":88},[71,129,130],{"class":88}," Server.sln",[71,132,133],{"class":88}," add",[71,135,136],{"class":88}," src\u002Fserver.csproj\n",[138,139,141],"h4",{"id":140},"configure-the-application-start-up","Configure the application start up",[12,143,144,145,148,149,152],{},"We selected the Web Api project template primarily because it is mostly configured for a web based project because\nthe ",[31,146,147],{},"*.csproj"," is configured to make use of ",[31,150,151],{},"Microsoft.NET.Sdk.Web","which contains all the middleware and functionality\nwe will need.",[12,154,155,156,159],{},"Once our project has been created we will then need to clear out all the junk that has been pre-populated in the\n",[31,157,158],{},"Program.cs"," file and simply replace it with the following code.",[60,161,169],{"className":162,"code":163,"filename":158,"highlights":164,"language":168,"meta":66,"style":67},"language-csharp shiki shiki-themes material-theme-lighter monokai monokai monokai","\nvar builder = WebApplication.CreateBuilder(args);\nvar app = builder.Build();\napp.UseWebSockets();\napp.Run();\n\n",[123,165,166,167],5,6,7,"csharp",[31,170,171,175,210,229,243],{"__ignoreMap":67},[71,172,173],{"class":73,"line":74},[71,174,78],{"emptyLinePlaceholder":77},[71,176,177,181,185,189,193,197,201,204,207],{"class":73,"line":81},[71,178,180],{"class":179},"soY9U","var",[71,182,184],{"class":183},"scfrN"," builder",[71,186,188],{"class":187},"sb10O"," =",[71,190,192],{"class":191},"sRVMn"," WebApplication",[71,194,196],{"class":195},"s8B1f",".",[71,198,200],{"class":199},"s_vVk","CreateBuilder",[71,202,203],{"class":195},"(",[71,205,206],{"class":191},"args",[71,208,209],{"class":195},");\n",[71,211,212,214,217,219,221,223,226],{"class":73,"line":102},[71,213,180],{"class":179},[71,215,216],{"class":183}," app",[71,218,188],{"class":187},[71,220,184],{"class":191},[71,222,196],{"class":195},[71,224,225],{"class":199},"Build",[71,227,228],{"class":195},"();\n",[71,230,233,236,238,241],{"class":231,"line":123},[73,232],"highlight",[71,234,235],{"class":191},"app",[71,237,196],{"class":195},[71,239,240],{"class":199},"UseWebSockets",[71,242,228],{"class":195},[71,244,246,248,250,253],{"class":245,"line":165},[73,232],[71,247,235],{"class":191},[71,249,196],{"class":195},[71,251,252],{"class":199},"Run",[71,254,228],{"class":195},[12,256,257,258,261,262,265,266,269],{},"The key line in the code is the ",[31,259,260],{},"app.UseWebSockets()"," which adds the WebSockets middleware to the request pipeline,\nthis enables accepting WebSocket requests by  adding a ",[31,263,264],{},"IHttpWebSocketFeature"," to the ",[31,267,268],{},"HttpContext"," if the request is\na valid WebSocket request.",[138,271,273],{"id":272},"write-a-custom-notifier-class","Write a custom Notifier class",[12,275,276],{},"For our simple Notification utility we will implement a Notification class with Handle method to process whatever we\nreceive. What we'll do is configure a Buffer as storage location to receive any data from the WebSocket connection\nasynchronously.",[12,278,279],{},"The Handle method manages the process of receiving and sending messages via WebSocket connection. It takes a WebSocket\nobject ws as an argument. async denotes that this method contains asynchronous operations that return a task\nrepresenting the ongoing operation.",[12,281,282,285],{},[31,283,284],{},"var buffer = new byte[1024 * 4];"," initialises a byte array buffer with a size of 4096 bytes (1024 * 4) to hold\nincoming data from the WebSocket client.",[12,287,288,291,292,295,296,299,300,302],{},[31,289,290],{},"ReceiveAsync"," is an asynchronous method that receives data from the WebSocket into the buffer. It returns an instance of\n",[31,293,294],{},"WebSocketReceiveResult",". The ",[31,297,298],{},"await"," keyword is used to pause the execution of the method until the ",[31,301,290],{},"\noperation completes.",[12,304,305,306,309],{},"We initiate a loop that continues as long as the WebSocket's ",[31,307,308],{},"CloseStatus"," is null, inferring that the connection is\nstill open.",[12,311,312,313,316],{},"Inside the loop, the server sends back the message it just received. ",[31,314,315],{},"ws.SendAsync"," is used to send data to the WebSocket client\nOnce the data has been received we will immediately send it on again to whichever client it connected to the websocket.\nThe loop repeats as long as the client continues to send messages without closing the connection.",[12,318,319,320,323],{},"After the client closes the connection, the server also closes from its side using the CloseAsync method of the\n",[31,321,322],{},"WebSocket"," ws object. This method is provided with the status of the reason for closing (obtained from the client)\nand a cancellation token.",[12,325,326],{},"This method is typically used in a WebSocket-enabled Server to handle bidirectional communication that a WebSocket\nconnection offers. Reception and transmission of messages are covered within this method.",[60,328,332],{"className":162,"code":329,"filename":330,"highlights":331,"language":168,"meta":66,"style":67},"\npublic class Notifier\n{\n    public async Task Handle(WebSocket ws)\n    {\n        var buffer = new byte[1024 * 4];\n        var receiveResult = await ws.ReceiveAsync(\n            new ArraySegment\u003Cbyte>(buffer), CancellationToken.None);\n\n        while (!receiveResult.CloseStatus.HasValue)\n        {\n            await ws.SendAsync(\n                new ArraySegment\u003Cbyte>(buffer, 0, receiveResult.Count),\n                receiveResult.MessageType,\n                receiveResult.EndOfMessage,\n                CancellationToken.None);\n\n            receiveResult = await ws.ReceiveAsync(\n                new ArraySegment\u003Cbyte>(buffer), CancellationToken.None);\n        }\n        await ws.CloseAsync(\n            receiveResult.CloseStatus.Value,\n            receiveResult.CloseStatusDescription,\n            CancellationToken.None);  \n    }\n}\n\n","Notifier.cs",[123,165,166,167],[31,333,334,338,351,356,381,387,419,441,475,480,507,513,528,562,576,588,600,605,624,649,655,670,687,699,715,721],{"__ignoreMap":67},[71,335,336],{"class":73,"line":74},[71,337,78],{"emptyLinePlaceholder":77},[71,339,340,344,347],{"class":73,"line":81},[71,341,343],{"class":342},"s_nUS","public",[71,345,346],{"class":179}," class",[71,348,350],{"class":349},"sIQzR"," Notifier\n",[71,352,353],{"class":73,"line":102},[71,354,355],{"class":195},"{\n",[71,357,359,362,365,368,371,373,375,378],{"class":358,"line":123},[73,232],[71,360,361],{"class":342},"    public",[71,363,364],{"class":342}," async",[71,366,367],{"class":349}," Task",[71,369,370],{"class":199}," Handle",[71,372,203],{"class":195},[71,374,322],{"class":349},[71,376,377],{"class":183}," ws",[71,379,380],{"class":195},")\n",[71,382,384],{"class":383,"line":165},[73,232],[71,385,386],{"class":195},"    {\n",[71,388,390,393,396,398,400,403,406,410,413,416],{"class":389,"line":166},[73,232],[71,391,392],{"class":179},"        var",[71,394,395],{"class":183}," buffer",[71,397,188],{"class":187},[71,399,89],{"class":187},[71,401,402],{"class":187}," byte",[71,404,405],{"class":195},"[",[71,407,409],{"class":408},"so6kZ","1024",[71,411,412],{"class":187}," *",[71,414,415],{"class":408}," 4",[71,417,418],{"class":195},"];\n",[71,420,422,424,427,429,432,434,436,438],{"class":421,"line":167},[73,232],[71,423,392],{"class":179},[71,425,426],{"class":183}," receiveResult",[71,428,188],{"class":187},[71,430,431],{"class":187}," await",[71,433,377],{"class":191},[71,435,196],{"class":195},[71,437,290],{"class":199},[71,439,440],{"class":195},"(\n",[71,442,444,447,450,453,456,459,462,465,468,470,473],{"class":73,"line":443},8,[71,445,446],{"class":187},"            new",[71,448,449],{"class":349}," ArraySegment",[71,451,452],{"class":195},"\u003C",[71,454,455],{"class":187},"byte",[71,457,458],{"class":195},">(",[71,460,461],{"class":191},"buffer",[71,463,464],{"class":195},"),",[71,466,467],{"class":191}," CancellationToken",[71,469,196],{"class":195},[71,471,472],{"class":191},"None",[71,474,209],{"class":195},[71,476,478],{"class":73,"line":477},9,[71,479,78],{"emptyLinePlaceholder":77},[71,481,483,487,490,493,496,498,500,502,505],{"class":73,"line":482},10,[71,484,486],{"class":485},"s7SBB","        while",[71,488,489],{"class":195}," (",[71,491,492],{"class":187},"!",[71,494,495],{"class":191},"receiveResult",[71,497,196],{"class":195},[71,499,308],{"class":191},[71,501,196],{"class":195},[71,503,504],{"class":191},"HasValue",[71,506,380],{"class":195},[71,508,510],{"class":73,"line":509},11,[71,511,512],{"class":195},"        {\n",[71,514,516,519,521,523,526],{"class":73,"line":515},12,[71,517,518],{"class":187},"            await",[71,520,377],{"class":191},[71,522,196],{"class":195},[71,524,525],{"class":199},"SendAsync",[71,527,440],{"class":195},[71,529,531,534,536,538,540,542,544,547,550,552,554,556,559],{"class":73,"line":530},13,[71,532,533],{"class":187},"                new",[71,535,449],{"class":349},[71,537,452],{"class":195},[71,539,455],{"class":187},[71,541,458],{"class":195},[71,543,461],{"class":191},[71,545,546],{"class":195},",",[71,548,549],{"class":408}," 0",[71,551,546],{"class":195},[71,553,426],{"class":191},[71,555,196],{"class":195},[71,557,558],{"class":191},"Count",[71,560,561],{"class":195},"),\n",[71,563,565,568,570,573],{"class":73,"line":564},14,[71,566,567],{"class":191},"                receiveResult",[71,569,196],{"class":195},[71,571,572],{"class":191},"MessageType",[71,574,575],{"class":195},",\n",[71,577,579,581,583,586],{"class":73,"line":578},15,[71,580,567],{"class":191},[71,582,196],{"class":195},[71,584,585],{"class":191},"EndOfMessage",[71,587,575],{"class":195},[71,589,591,594,596,598],{"class":73,"line":590},16,[71,592,593],{"class":191},"                CancellationToken",[71,595,196],{"class":195},[71,597,472],{"class":191},[71,599,209],{"class":195},[71,601,603],{"class":73,"line":602},17,[71,604,78],{"emptyLinePlaceholder":77},[71,606,608,611,614,616,618,620,622],{"class":73,"line":607},18,[71,609,610],{"class":191},"            receiveResult ",[71,612,613],{"class":187},"=",[71,615,431],{"class":187},[71,617,377],{"class":191},[71,619,196],{"class":195},[71,621,290],{"class":199},[71,623,440],{"class":195},[71,625,627,629,631,633,635,637,639,641,643,645,647],{"class":73,"line":626},19,[71,628,533],{"class":187},[71,630,449],{"class":349},[71,632,452],{"class":195},[71,634,455],{"class":187},[71,636,458],{"class":195},[71,638,461],{"class":191},[71,640,464],{"class":195},[71,642,467],{"class":191},[71,644,196],{"class":195},[71,646,472],{"class":191},[71,648,209],{"class":195},[71,650,652],{"class":73,"line":651},20,[71,653,654],{"class":195},"        }\n",[71,656,658,661,663,665,668],{"class":73,"line":657},21,[71,659,660],{"class":187},"        await",[71,662,377],{"class":191},[71,664,196],{"class":195},[71,666,667],{"class":199},"CloseAsync",[71,669,440],{"class":195},[71,671,673,676,678,680,682,685],{"class":73,"line":672},22,[71,674,675],{"class":191},"            receiveResult",[71,677,196],{"class":195},[71,679,308],{"class":191},[71,681,196],{"class":195},[71,683,684],{"class":191},"Value",[71,686,575],{"class":195},[71,688,690,692,694,697],{"class":73,"line":689},23,[71,691,675],{"class":191},[71,693,196],{"class":195},[71,695,696],{"class":191},"CloseStatusDescription",[71,698,575],{"class":195},[71,700,702,705,707,709,712],{"class":73,"line":701},24,[71,703,704],{"class":191},"            CancellationToken",[71,706,196],{"class":195},[71,708,472],{"class":191},[71,710,711],{"class":195},");",[71,713,714],{"class":191},"  \n",[71,716,718],{"class":73,"line":717},25,[71,719,720],{"class":195},"    }\n",[71,722,724],{"class":73,"line":723},26,[71,725,726],{"class":195},"}\n",[138,728,730],{"id":729},"implement-websocket-controller","Implement websocket controller",[12,732,733],{},"Once we have completed our Notifier, we can turn our attention to writing our Controller. Although you can also implement\nthis making use of Minimal API, we'll implement making use of the typical dotnet based controller.",[12,735,736,737,196],{},"The code will check if the incoming HTTP request is a WebSocket request ",[31,738,739],{},"(HttpContext.WebSockets.IsWebSocketRequest)",[12,741,742],{},"If it is a WebSocket request:",[744,745,746,753],"ul",{},[747,748,749,750,196],"li",{},"Accept the WebSocket connection ",[31,751,752],{},"(await HttpContext.WebSockets.AcceptWebSocketAsync())",[747,754,755],{},"Hands over the accepted WebSocket to the Handle method of the notifier object. The Handle method, defined in the\nNotifier class, handles the incoming WebSocket messages in a loop until the WebSocket is closed from the other end.\nMessages received are echoed back to the client, real-time.",[12,757,758],{},"If it is not a WebSocket request:",[744,760,761],{},[747,762,763],{},"It sets the HTTP response status code to 400 Bad Request.",[60,765,768],{"className":162,"code":766,"filename":767,"language":168,"meta":66,"style":67},"\npublic class WebSocketController(Notifier notifier) : ControllerBase\n{\n    [Route(\"\u002F\")]\n    public async Task Get()\n    {\n        if (HttpContext.WebSockets.IsWebSocketRequest)\n        {\n            using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();\n            await notifier.Handle(webSocket);\n        }\n        else\n        {\n            HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;\n        }\n    }\n}\n\n","WebSocketController.cs",[31,769,770,774,800,804,826,840,844,865,869,898,916,920,925,929,957,961,965],{"__ignoreMap":67},[71,771,772],{"class":73,"line":74},[71,773,78],{"emptyLinePlaceholder":77},[71,775,776,778,780,783,785,788,791,794,797],{"class":73,"line":81},[71,777,343],{"class":342},[71,779,346],{"class":179},[71,781,782],{"class":349}," WebSocketController",[71,784,203],{"class":195},[71,786,787],{"class":349},"Notifier",[71,789,790],{"class":183}," notifier",[71,792,793],{"class":195},")",[71,795,796],{"class":195}," :",[71,798,799],{"class":349}," ControllerBase\n",[71,801,802],{"class":73,"line":102},[71,803,355],{"class":195},[71,805,806,809,812,814,818,821,823],{"class":73,"line":123},[71,807,808],{"class":195},"    [",[71,810,811],{"class":349},"Route",[71,813,203],{"class":195},[71,815,817],{"class":816},"sxJtY","\"",[71,819,820],{"class":88},"\u002F",[71,822,817],{"class":816},[71,824,825],{"class":195},")]\n",[71,827,828,830,832,834,837],{"class":73,"line":165},[71,829,361],{"class":342},[71,831,364],{"class":342},[71,833,367],{"class":349},[71,835,836],{"class":199}," Get",[71,838,839],{"class":195},"()\n",[71,841,842],{"class":73,"line":166},[71,843,386],{"class":195},[71,845,846,849,851,853,855,858,860,863],{"class":73,"line":167},[71,847,848],{"class":485},"        if",[71,850,489],{"class":195},[71,852,268],{"class":191},[71,854,196],{"class":195},[71,856,857],{"class":191},"WebSockets",[71,859,196],{"class":195},[71,861,862],{"class":191},"IsWebSocketRequest",[71,864,380],{"class":195},[71,866,867],{"class":73,"line":443},[71,868,512],{"class":195},[71,870,871,874,877,880,882,884,887,889,891,893,896],{"class":73,"line":477},[71,872,873],{"class":485},"            using",[71,875,876],{"class":179}," var",[71,878,879],{"class":183}," webSocket",[71,881,188],{"class":187},[71,883,431],{"class":187},[71,885,886],{"class":191}," HttpContext",[71,888,196],{"class":195},[71,890,857],{"class":191},[71,892,196],{"class":195},[71,894,895],{"class":199},"AcceptWebSocketAsync",[71,897,228],{"class":195},[71,899,900,902,904,906,909,911,914],{"class":73,"line":482},[71,901,518],{"class":187},[71,903,790],{"class":191},[71,905,196],{"class":195},[71,907,908],{"class":199},"Handle",[71,910,203],{"class":195},[71,912,913],{"class":191},"webSocket",[71,915,209],{"class":195},[71,917,918],{"class":73,"line":509},[71,919,654],{"class":195},[71,921,922],{"class":73,"line":515},[71,923,924],{"class":485},"        else\n",[71,926,927],{"class":73,"line":530},[71,928,512],{"class":195},[71,930,931,934,936,939,941,944,946,949,951,954],{"class":73,"line":564},[71,932,933],{"class":191},"            HttpContext",[71,935,196],{"class":195},[71,937,938],{"class":191},"Response",[71,940,196],{"class":195},[71,942,943],{"class":191},"StatusCode ",[71,945,613],{"class":187},[71,947,948],{"class":191}," StatusCodes",[71,950,196],{"class":195},[71,952,953],{"class":191},"Status400BadRequest",[71,955,956],{"class":195},";\n",[71,958,959],{"class":73,"line":578},[71,960,654],{"class":195},[71,962,963],{"class":73,"line":590},[71,964,720],{"class":195},[71,966,967],{"class":73,"line":602},[71,968,726],{"class":195},[138,970,972],{"id":971},"update-the-application-startup","Update the application startup",[12,974,975,976,978],{},"We can then update our ",[31,977,158],{},"to create an HTTP server that supports WebSockets, using the main ASP.NET Core\nbuilding blocks and .NET's built-in Dependency Injection.",[12,980,981,982],{},"Here is a line-by-line explanation:\n",[31,983,984],{},"var builder = WebApplication.CreateBuilder(args);",[12,986,987,988,991],{},"The ",[31,989,990],{},"WebApplication.CreateBuilder(args)"," is a static method that's used to create and configure a builder object for\na WebApplication. The created builder is used to configure services for the application and build the web application\nhost.",[12,993,994,997],{},[31,995,996],{},"builder.Services.AddControllers();"," adds services required for the MVC framework to the application's services,\nspecifically Controllers.",[12,999,1000,1003],{},[31,1001,1002],{},"builder.Services.AddSingleton\u003CNotifier>();"," statement is setting up a dependency injection for the Notifier class.\nSpecifically, it's telling the framework to create a single instance of Notifier for when any component of the\napplication requires it.",[12,1005,1006,1009,1010,1013],{},[31,1007,1008],{},"var app = builder.Build();"," builds the WebApplication using the configurations provided and returns a WebApplication object.\n",[31,1011,1012],{},"app.UseWebSockets();"," The middleware that enables the ability to service WebSocket requests. WebSockets allows\nreal-time, two-way communications between a client (like a browser) and a host server.",[12,1015,1016,1019],{},[31,1017,1018],{},"app.MapControllers();"," is an endpoint routing middleware that maps HTTP request routes to the matching action methods\nin the controllers.",[12,1021,1022,1025],{},[31,1023,1024],{},"app.Run();"," starts the application and begins listening for HTTP requests.",[12,1027,1028],{},"The Notifier class code represents an implementation of a class that allows a WebSocket server to handle and respond\nto messages from a WebSocket client. It reads the incoming messages in a continuous loop and echoes them back to the\nclient until the client disconnects.",[12,1030,1031],{},"The Handle method in the Notifier class is implemented asynchronously, providing the standard pattern for handling\nrequests and responses via WebSockets in ASP.NET Core.",[60,1033,1035],{"className":162,"code":1034,"filename":158,"language":168,"meta":66,"style":67},"\nvar builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddControllers();\nbuilder.Services.AddSingleton\u003CNotifier>();\n\nvar app = builder.Build();\napp.UseWebSockets();\napp.MapControllers();\n\napp.Run();\n\n",[31,1036,1037,1041,1061,1078,1098,1102,1118,1128,1139,1143],{"__ignoreMap":67},[71,1038,1039],{"class":73,"line":74},[71,1040,78],{"emptyLinePlaceholder":77},[71,1042,1043,1045,1047,1049,1051,1053,1055,1057,1059],{"class":73,"line":81},[71,1044,180],{"class":179},[71,1046,184],{"class":183},[71,1048,188],{"class":187},[71,1050,192],{"class":191},[71,1052,196],{"class":195},[71,1054,200],{"class":199},[71,1056,203],{"class":195},[71,1058,206],{"class":191},[71,1060,209],{"class":195},[71,1062,1063,1066,1068,1071,1073,1076],{"class":73,"line":102},[71,1064,1065],{"class":191},"builder",[71,1067,196],{"class":195},[71,1069,1070],{"class":191},"Services",[71,1072,196],{"class":195},[71,1074,1075],{"class":199},"AddControllers",[71,1077,228],{"class":195},[71,1079,1080,1082,1084,1086,1088,1091,1093,1095],{"class":73,"line":123},[71,1081,1065],{"class":191},[71,1083,196],{"class":195},[71,1085,1070],{"class":191},[71,1087,196],{"class":195},[71,1089,1090],{"class":199},"AddSingleton",[71,1092,452],{"class":195},[71,1094,787],{"class":349},[71,1096,1097],{"class":195},">();\n",[71,1099,1100],{"class":73,"line":165},[71,1101,78],{"emptyLinePlaceholder":77},[71,1103,1104,1106,1108,1110,1112,1114,1116],{"class":73,"line":166},[71,1105,180],{"class":179},[71,1107,216],{"class":183},[71,1109,188],{"class":187},[71,1111,184],{"class":191},[71,1113,196],{"class":195},[71,1115,225],{"class":199},[71,1117,228],{"class":195},[71,1119,1120,1122,1124,1126],{"class":73,"line":167},[71,1121,235],{"class":191},[71,1123,196],{"class":195},[71,1125,240],{"class":199},[71,1127,228],{"class":195},[71,1129,1130,1132,1134,1137],{"class":73,"line":443},[71,1131,235],{"class":191},[71,1133,196],{"class":195},[71,1135,1136],{"class":199},"MapControllers",[71,1138,228],{"class":195},[71,1140,1141],{"class":73,"line":477},[71,1142,78],{"emptyLinePlaceholder":77},[71,1144,1145,1147,1149,1151],{"class":73,"line":482},[71,1146,235],{"class":191},[71,1148,196],{"class":195},[71,1150,252],{"class":199},[71,1152,228],{"class":195},[138,1154,1156],{"id":1155},"test-the-websocket","Test the websocket",[12,1158,1159],{},"All the development we need to implement a websocket is now complete, we can now get ready to test our websocket.",[12,1161,1162,1163,1168,1169,1174,1175,1180],{},"we generally make use of ",[16,1164,1167],{"href":1165,"rel":1166},"https:\u002F\u002Fwww.jetbrains.com\u002F",[20],"Jetbrains IDE"," for all our development work, irrespective of language or platform and for\ndotnet based development we make use of ",[16,1170,1173],{"href":1171,"rel":1172},"https:\u002F\u002Fwww.jetbrains.com\u002Frider\u002F",[20],"Jetbrains Rider",". One of the many reasons why\nwe do this is because of all the great tools that are built directly into the IDE and ready to use.  One of these tools\nis the built-in support for ",[16,1176,1179],{"href":1177,"rel":1178},"https:\u002F\u002Fwww.jetbrains.com\u002Fhelp\u002Frider\u002FHttp_client_in__product__code_editor.html",[20],"HTTP Client",",\nthis feature enables you to create, edit, and execute HTTP requests directly in the JetBrains Rider code editor.",[12,1182,1183],{},"We are going to use the feature to create a tests for our websocket.  The way we typically do this is to add an Integration\ntest project to our solution.",[60,1185,1187],{"className":62,"code":1186,"filename":64,"language":65,"meta":66,"style":67},"dotnet new xunit --name Integration --output tests\u002Fintegration\ndotnet sln dotnet sln Server.sln add tests\u002Fintegration\u002FIntegration.csproj\n",[31,1188,1189,1208],{"__ignoreMap":67},[71,1190,1191,1193,1195,1198,1200,1203,1205],{"class":73,"line":74},[71,1192,85],{"class":84},[71,1194,89],{"class":88},[71,1196,1197],{"class":88}," xunit",[71,1199,96],{"class":95},[71,1201,1202],{"class":88}," Integration",[71,1204,117],{"class":95},[71,1206,1207],{"class":88}," tests\u002Fintegration\n",[71,1209,1210,1212,1214,1217,1219,1221,1223],{"class":73,"line":81},[71,1211,85],{"class":84},[71,1213,92],{"class":88},[71,1215,1216],{"class":88}," dotnet",[71,1218,92],{"class":88},[71,1220,130],{"class":88},[71,1222,133],{"class":88},[71,1224,1225],{"class":88}," tests\u002Fintegration\u002FIntegration.csproj\n",[12,1227,1228,1229,1232,1233,1236],{},"This will add a unit test project to our solution, for our immediate purposes we are not going to use the xunit test\nfunctionality, so you can go ahead and delete the ",[31,1230,1231],{},"GlobalUsings.cs"," and ",[31,1234,1235],{},"UnitTest1.cs","  files because we won't be\nneeding them.",[12,1238,1239],{},"We can now add a Http Request file then you can add the following code that uses an HTTP request code for a WebSocket\nconnection to the server.",[12,1241,1242,1243,1246],{},"This line of the code indicates the use of the WebSocket protocol to open a two-way interactive communication session\nbetween the user's browser and a server, with ",[31,1244,1245],{},"ws:\u002F\u002Flocalhost:5175"," being the address of the server. WebSocket works over\nHTTP ports 80 and 443 as well as to support HTTP proxies and intermediaries.",[12,1248,987,1249,1252,1253,1256],{},[31,1250,1251],{},"Content-Type"," header is used to indicate the media type of the resource. ",[31,1254,1255],{},"application\u002Fjson"," means that the content of\nthe transmitted data in the WebSocket will be in JSON format.",[60,1258,1262],{"className":1259,"code":1260,"filename":64,"language":1261,"meta":66,"style":67},"language-http shiki shiki-themes material-theme-lighter monokai monokai monokai","\nWEBSOCKET ws:\u002F\u002Flocalhost:5175\nContent-Type: text\u002Fplain\n\n","http",[31,1263,1264,1268,1273],{"__ignoreMap":67},[71,1265,1266],{"class":73,"line":74},[71,1267,78],{"emptyLinePlaceholder":77},[71,1269,1270],{"class":73,"line":81},[71,1271,1272],{},"WEBSOCKET ws:\u002F\u002Flocalhost:5175\n",[71,1274,1275],{"class":73,"line":102},[71,1276,1277],{},"Content-Type: text\u002Fplain\n",[12,1279,1280,1281],{},"A full in depth discussion on how to write fully functioning websocket tests with the HTTP Requests, is beyond the scope of this\ntutorial, and ",[16,1282,1285],{"href":1283,"rel":1284},"https:\u002F\u002Fwww.jetbrains.com\u002Fhelp\u002Frider\u002FHttp_client_in__product__code_editor.html#websocket",[20],"Jetbrains a great resource to help you get started",[12,1287,1288],{},"We can now start our project, and when it's all running we can navigate to the file in our IDE and click the green\narrow to connect to our Websocket. We should see the connection happen and the confirmation in the window.",[1290,1291],"content-image",{"image":1292,"title":1293},"websocket-test_axes1t","Rider Endpoint tests for web socket",[12,1295,1296,1297,1300],{},"With the connection established, we can now interact with our websocket by typing in the Message to be sent to Websocket\nbox and pressing ",[31,1298,1299],{},"Ctrl + Enter"," to send it.  We should see the socket receive and then automatically resend the message.",[1290,1302],{"image":1303,"title":1293},"websoket-test2_prwp9l",[44,1305,1307],{"id":1306},"conclusion","Conclusion",[12,1309,1310],{},"If you have followed along with this post, you should have a basis of a functioning websocket server up and running,\nthere is a lot more you can do and a few more configuration options you can configure for the websocket, but you have the\nbasic all configured for you to continue developing.",[1312,1313,1314],"style",{},"html pre.shiki code .sC-bC, html code.shiki .sC-bC{--shiki-light:#E2931D;--shiki-default:#A6E22E;--shiki-dark:#A6E22E;--shiki-sepia:#A6E22E}html pre.shiki code .sKdu0, html code.shiki .sKdu0{--shiki-light:#91B859;--shiki-default:#E6DB74;--shiki-dark:#E6DB74;--shiki-sepia:#E6DB74}html pre.shiki code .sbSe9, html code.shiki .sbSe9{--shiki-light:#91B859;--shiki-default:#AE81FF;--shiki-dark:#AE81FF;--shiki-sepia:#AE81FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .soY9U, html code.shiki .soY9U{--shiki-light:#E2931D;--shiki-light-font-style:inherit;--shiki-default:#66D9EF;--shiki-default-font-style:italic;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic;--shiki-sepia:#66D9EF;--shiki-sepia-font-style:italic}html pre.shiki code .scfrN, html code.shiki .scfrN{--shiki-light:#E2931D;--shiki-default:#F8F8F2;--shiki-dark:#F8F8F2;--shiki-sepia:#F8F8F2}html pre.shiki code .sb10O, html code.shiki .sb10O{--shiki-light:#39ADB5;--shiki-default:#F92672;--shiki-dark:#F92672;--shiki-sepia:#F92672}html pre.shiki code .sRVMn, html code.shiki .sRVMn{--shiki-light:#90A4AE;--shiki-default:#F8F8F2;--shiki-dark:#F8F8F2;--shiki-sepia:#F8F8F2}html pre.shiki code .s8B1f, html code.shiki .s8B1f{--shiki-light:#39ADB5;--shiki-default:#F8F8F2;--shiki-dark:#F8F8F2;--shiki-sepia:#F8F8F2}html pre.shiki code .s_vVk, html code.shiki .s_vVk{--shiki-light:#6182B8;--shiki-default:#A6E22E;--shiki-dark:#A6E22E;--shiki-sepia:#A6E22E}html pre.shiki code .s_nUS, html code.shiki .s_nUS{--shiki-light:#9C3EDA;--shiki-default:#F92672;--shiki-dark:#F92672;--shiki-sepia:#F92672}html pre.shiki code .sIQzR, html code.shiki .sIQzR{--shiki-light:#E2931D;--shiki-light-text-decoration:inherit;--shiki-default:#A6E22E;--shiki-default-text-decoration:underline;--shiki-dark:#A6E22E;--shiki-dark-text-decoration:underline;--shiki-sepia:#A6E22E;--shiki-sepia-text-decoration:underline}html pre.shiki code .so6kZ, html code.shiki .so6kZ{--shiki-light:#F76D47;--shiki-default:#AE81FF;--shiki-dark:#AE81FF;--shiki-sepia:#AE81FF}html pre.shiki code .s7SBB, html code.shiki .s7SBB{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#F92672;--shiki-default-font-style:inherit;--shiki-dark:#F92672;--shiki-dark-font-style:inherit;--shiki-sepia:#F92672;--shiki-sepia-font-style:inherit}html pre.shiki code .sxJtY, html code.shiki .sxJtY{--shiki-light:#39ADB5;--shiki-default:#E6DB74;--shiki-dark:#E6DB74;--shiki-sepia:#E6DB74}",{"title":67,"searchDepth":81,"depth":81,"links":1316},[1317,1318],{"id":46,"depth":102,"text":47},{"id":1306,"depth":102,"text":1307},"development","2024-05-15","A walk-through guide on how to implement WebSocket end points in dotnet","md",{"url":1324,"title":5},"v1719908486\u002Fwebsockets_pjqtld",{"publishDateTime":1326},"2023-05-15:17:30","\u002Fposts\u002Fhow-to-create-a-websocket-server-in-dotnet",{"title":5,"description":1321},{"loc":1327,"lastmod":1320,"changefreq":1330,"priority":1331,"images":1332},"monthly",0.8,[1333,1335,1337],{"loc":1334},"https:\u002F\u002Fres.cloudinary.com\u002Fthreenine-co-uk\u002Fimage\u002Fupload\u002Ff_jpg,w_1200\u002Fv1719908486\u002Fwebsockets_pjqtld",{"loc":1336},"https:\u002F\u002Fres.cloudinary.com\u002Fthreenine-co-uk\u002Fimage\u002Fupload\u002Ff_jpg,w_1200\u002Fwebsocket-test_axes1t",{"loc":1338},"https:\u002F\u002Fres.cloudinary.com\u002Fthreenine-co-uk\u002Fimage\u002Fupload\u002Ff_jpg,w_1200\u002Fwebsoket-test2_prwp9l","posts\u002Fhow-to-create-a-websocket-server-in-dotnet","An introduction to web sockets in with dotnet","WebSockets provide a number of significant advantages to help implement Real-Time communication in applications , they do require specific server side support and the your service application needs to support the WebSocket protocol.","A0uO3jOJYoqY-s9BcDoRDp3xd14bj28gCPYhCvPWUjQ",{"name":1344,"to":1345,"avatar":1346,"sameAs":1347},"Gary Woodfine","https:\u002F\u002Fgarywoodfine.com","garywoodfine_tlccjw",[1348,1349,1350,1351],"https:\u002F\u002Fgithub.com\u002Fgarywoodfine","https:\u002F\u002Fwww.youtube.com\u002F@Threenineio","https:\u002F\u002Fnjump.me\u002Fnpub1a3jz7w9laar2k77dkkh2rhxunr9zpja5emrl5yrxetamt29swqyqmmt32y","nostr:npub1a3jz7w9laar2k77dkkh2rhxunr9zpja5emrl5yrxetamt29swqyqmmt32y",[1353,1358],{"title":1354,"path":1355,"stem":1356,"description":1357,"children":-1},"How to add tailwindcss to nuxt","\u002Fposts\u002Fhow-to-add-tailwindcss-to-nuxt","posts\u002Fhow-to-add-tailwindcss-to-nuxt","Explore the Nuxt framework and how to start a new project using Nuxt and Vue",{"title":1359,"path":1360,"stem":1361,"description":1362,"children":-1},"How to store Terraform state in DigitalOcean Spaces","\u002Fposts\u002Fhow-to-store-terraform_state-in-digital-ocean-space","posts\u002Fhow-to-store-terraform_state-in-digital-ocean-space","using DigitalOcean Spaces as a Terraform state Backend",{"id":1364,"extension":1365,"items":1366,"meta":1408,"stem":1409,"__hash__":1410},"navigation\u002Fnavigation.yaml","yaml",[1367,1380,1393,1401],{"label":1368,"children":1369},"products",[1370,1375],{"label":1371,"icon":1372,"description":1373,"to":1374},"Diogel","material-symbols:preview","Keep your Nostr identity in your hands, in your browser, on your phone or on your own server.","\u002Fproducts\u002Fdiogel",{"label":1376,"icon":1377,"description":1378,"to":1379},"Nostrfi","i-lucide-radio-tower","Nostr relay, storage and backend-service infrastructure for builders and operators.","\u002Fproducts\u002Fnostrfi",{"label":1381,"children":1382},"services",[1383,1388],{"label":1384,"description":1385,"icon":1386,"to":1387},"Nostr product development","Focused engineering support for teams building Nostr-native products, tools and protocol-aware user experiences.","game-icons:ostrich","\u002Fservices\u002Fnostr-product-development",{"label":1389,"description":1390,"icon":1391,"to":1392},"Bitcoin product development","Engineering for Bitcoin payments, wallet-connected flows and products where users hold their own keys.","bx:bxl-bitcoin","\u002Fservices\u002Fbitcoin-product-development",{"label":1394,"children":1395},"company",[1396],{"label":1397,"icon":1398,"description":1399,"to":1400},"About","i-lucide-file-text","What we believe, what we build and how we work.","\u002Fcompany\u002Fabout",{"label":1402,"children":1403},"resources",[1404],{"label":1405,"icon":1398,"description":1406,"to":1407},"Blog","Practical Bitcoin, Nostr and product engineering guides.","https:\u002F\u002Fthreenine.blog",{},"navigation","n-Xc9COCwoCHJiMCeBNLauHtuhxcGol-wu9S2og3Rqs",{"id":1412,"title":1344,"avatar":1346,"body":1413,"description":1417,"extension":1322,"meta":1432,"name":1344,"navigation":77,"path":1433,"role":1434,"sameAs":1435,"seo":1436,"stem":1437,"summary":1438,"to":1345,"username":7,"__hash__":1439},"authors\u002Fauthors\u002FGary Woodfine.md",{"type":9,"value":1414,"toc":1430},[1415,1418,1421,1424,1427],[12,1416,1417],{},"Polyglot software engineer with extensive experience in software engineering, with a passion to build great scalable software to solve complex business problems. A track record in developing high-performance backend systems, optimizing architecture, and ensuring continuous availability of critical APIs.",[12,1419,1420],{},"I'm an independent software developer, vendor, architect, consultant, blogger, Bitcoin & Nostr Enthusiast. I've provided software development expertise and helped augment software development skills and teams for a number of startups, product companies, corporate IT shops, and consulting firms.",[12,1422,1423],{},"I started out in the software world back in 1995, building websites with HoTMetaL Pro & ASP and moved on to building web apps with .NET when it released in Beta in 2001, since then, I've shipped systems in just about every industry.",[12,1425,1426],{},"Along my journey, I have also shipped various platforms & products in a number of other programming languages or software development frameworks that have had their moments in the sun, but somehow I have always made way back to C# & .Net.",[12,1428,1429],{},"Over the years, I've strived to improve how I and my teams deliver value to the clients, customers, and end-users. That has led me down the path of exploring Agile, Domain-Driven Design, REST, messaging, distributed systems, and anything else I thought could help.",{"title":67,"searchDepth":81,"depth":81,"links":1431},[],{},"\u002Fauthors\u002Fgary-woodfine","Managing Director",[1348,1349,1350,1351],{"title":1344,"description":1417},"authors\u002FGary Woodfine","independent software developer, vendor, architect, consultant, blogger, Bitcoin & Nostr Enthusiast. I've provided software development expertise and helped augment software development skills and teams for a number of startups, product companies, corporate IT shops, and consulting firms.","30ZWJ-6T2g8S0kLLgUhZWA7_QFUOpa5CElif7tAhsLo",{"title":1344,"name":1344,"role":1434,"summary":1438,"avatar":1346,"username":7,"to":1345},[1442,1454,1465,1472,1479,1486,1497,1506,1512,1519,1525,1532,1542,1551,1557,1563,1568,1573,1582,1588,1594,1598,1602,1605,1610,1615,1620,1625,1631,1636,1643,1652,1654,1659,1667,1672,1678,1683,1688,1692,1698,1703,1707],{"path":1443,"title":1444,"date":1445,"tags":1446,"category":1447,"featureImage":1452},"\u002Fposts\u002Fwhat-is-a-nostr-client","What is a Nostr client?","2026-10-26",[1447,1448,1449,1450,1451],"nostr","nostr-client","privacy","keys","getting-started",{"url":1453,"title":1444},"v1690313893\u002Fnostr-logo_hwrql1",{"path":1455,"title":1456,"date":1457,"tags":1458,"category":1447,"featureImage":1463},"\u002Fposts\u002Fhow-to-choose-nostr-relays","How to Choose Nostr Relays","2026-04-11",[1447,1459,1449,1460,1461,1462],"relay","security","nostr-relays","censorship-resistance",{"url":1453,"title":1464},"How to choose nostr relays",{"path":1466,"title":1467,"date":1457,"tags":1468,"category":1447,"featureImage":1471},"\u002Fposts\u002Fnostr-relays-privacy-and-security","Nostr Relays, Privacy, and Security",[1447,1459,1449,1460,1469,1470],"key-management","nostr-relay",{"url":1453,"title":1467},{"path":1473,"title":1474,"date":1457,"tags":1475,"category":1447,"featureImage":1477},"\u002Fposts\u002Fwhat-is-a-nostr-relay","What is a Nostr Relay?",[1447,1459,1449,1462,1476,1470],"decentralization",{"url":1453,"title":1478}," What is a Nostr Relay?",{"path":1480,"title":1481,"date":1482,"tags":1483,"category":1447,"featureImage":1485},"\u002Fposts\u002Fwhy-nostr-keys-better-privacy","Why Nostr keys and identity are better for privacy","2026-04-06",[1447,1449,1484,1460],"identity",{"url":1453,"title":1481},{"path":1487,"title":1488,"date":1489,"tags":1490,"category":1447,"featureImage":1496},"\u002Fposts\u002Fbest-nostr-browser-extensions-2026","Best Nostr Browser Extensions (2026): Complete Comparison","2026-03-24",[1447,1491,1492,1460,1493,1494,1495],"browser-extension","nip-07","comparison","porwr","diogel",{"url":1453,"title":1488},{"path":1498,"title":1499,"date":1489,"tags":1500,"category":1447,"featureImage":1503},"\u002Fposts\u002Fhow-to-get-started-with-porwr","Getting Started with Nostr: Porwr Setup Guide (2026)",[1501,1449,1447,1494,1495,1502,1492,1491],"social","cryptography",{"url":1504,"title":1505},"v1690313893\u002Fblog\u002Fdiogel-product-header_x3az6t","Porwr Browser Extension",{"path":1507,"title":1508,"date":1509,"tags":1510,"category":1447,"featureImage":1511},"\u002Fposts\u002Fhow-to-manage-keys-for-nostr","How to manage keys for Nostr","2025-12-15",[1447,1450,1449,1460],{"url":1453,"title":1508},{"path":1513,"title":1514,"date":1509,"tags":1515,"category":1447,"featureImage":1518},"\u002Fposts\u002Fhow-to-self-host-your-nip-05-identity","How to self-host your NIP-05 identity",[1447,1516,1449,1517],"nip-05","self-hosting",{"url":1453,"title":1514},{"path":1520,"title":1521,"date":1522,"tags":6,"category":1449,"featureImage":1523},"\u002Fposts\u002Fwhy-privacy-is-a-fundamental-human-right","Why privacy is a fundamental human right","2025-12-09",{"url":1524,"title":1521},"v1690313893\u002Fprivacy-header_exiblo",{"path":1526,"title":1527,"date":1528,"tags":6,"category":1529,"featureImage":1530},"\u002Fposts\u002Fhow-government-monetary-policy-erodes-the-value-of-money","How government monetary policy erodes the value of money","2025-12-01","bitcoin",{"url":1531,"title":1527},"v1719908027\u002Fbitcoin_szcm1m",{"path":1533,"title":1534,"date":1535,"tags":1536,"category":1529,"featureImage":1540},"\u002Fposts\u002Fbitaxe-touch-genesis-batch","Bitaxe Touch Genesis Batch","2025-07-23",[1537,1538,1539],"bitaxe","mining","solo",{"url":1541,"title":1534},"v1690313878\u002Fbitaxe-touch_c6qrjd",{"path":1543,"title":1544,"date":1545,"tags":1546,"category":1529,"featureImage":1550},"\u002Fposts\u002Fbitcoin-is-not-a-ponzi-scheme","Bitcoin is not a Ponzi Scheme","2025-07-22",[1547,1548,1549],"ponzi","economics","investment",{"url":1531,"title":1544},{"path":1552,"title":1553,"date":1545,"tags":1554,"category":1529,"featureImage":1556},"\u002Fposts\u002Fgetting-started-with-public-pool-mining","Getting started with Public Pool Mining",[1538,1539,1555],"lottery",{"url":1531,"title":1553},{"path":1558,"title":1559,"date":1560,"tags":1561,"category":1529,"featureImage":1562},"\u002Fposts\u002Fgetting-started-with-solo-mining","Getting started with solo mining","2025-07-19",[1537,1538,1539,1555],{"url":1531,"title":1559},{"path":1564,"title":1565,"date":1560,"tags":1566,"category":1529,"featureImage":1567},"\u002Fposts\u002Fwhat-is-bitcoin-mining","What is Bitcoin mining",[1538,1529],{"url":1531,"title":1565},{"path":1569,"title":1570,"date":1560,"tags":1571,"category":1529,"featureImage":1572},"\u002Fposts\u002Fwhat-is-solo-mining","What is solo mining",[1539],{"url":1531,"title":1570},{"path":1574,"title":1575,"date":1576,"tags":1577,"category":1529,"featureImage":1580},"\u002Fposts\u002Fhow-to-run-a-bitcoin-node","How to run a Bitcoin Node with Umbrel Home","2025-07-14",[1578,1579],"node","umbrel",{"url":1531,"title":1581},"How to run a Bitcoin node",{"path":1583,"title":1584,"date":1585,"tags":1586,"category":1447,"featureImage":1587},"\u002Fposts\u002Fhow-to-get-started-with-nostr","How to get started with Nostr","2024-10-09",[1447,1451,1449,1450],{"url":1453,"title":1584},{"path":1589,"title":1590,"date":1591,"tags":6,"category":1319,"featureImage":1592},"\u002Fposts\u002Fhow-to-use-layouts-and-pages-in-nuxt","How to use layouts and pages in Nuxt","2024-10-08",{"url":1593,"title":1590},"v1690313878\u002Fnuxt-header_tbc9cp",{"path":1355,"title":1354,"date":1595,"tags":6,"category":1319,"featureImage":1596},"2024-10-07",{"url":1593,"title":1597},"Getting started with Nuxt",{"path":1599,"title":1597,"date":1600,"tags":6,"category":1319,"featureImage":1601},"\u002Fposts\u002Fgetting-started-with-nuxt","2024-10-06",{"url":1593,"title":1597},{"path":1360,"title":1359,"date":1600,"tags":6,"category":1319,"featureImage":1603},{"url":1604,"title":1359},"v1690313893\u002Fterraform-header_ixmkgg",{"path":1606,"title":1607,"date":1608,"tags":6,"category":1319,"featureImage":1609},"\u002Fposts\u002Fhow-to-use-terraform-with-digitalocean","How to use Terraform with DigitalOcean","2024-10-05",{"url":1604,"title":1607},{"path":1611,"title":1612,"date":1613,"tags":6,"category":1449,"featureImage":1614},"\u002Fposts\u002Fhow-to-add-fathom-analytics-to-nuxt","How to add Fathom Analytics to Nuxt","2024-07-01",{"url":1593,"title":1612},{"path":1616,"title":1617,"date":1613,"tags":6,"category":1449,"featureImage":1618},"\u002Fposts\u002Fimportance-of-privacy-in-web-analytics","The Importance of Privacy in Web Analytics",{"url":1619,"title":1617},"v1690313893\u002Fanalytics_nlew9f",{"path":1621,"title":1622,"date":1613,"tags":1623,"category":1447,"featureImage":1624},"\u002Fposts\u002Fwhat-is-a-nip-07-signer","What is a NIP-07 signer?",[1447,1492,1491,1460],{"url":1453,"title":1622},{"path":1626,"title":1627,"date":1613,"tags":1628,"category":1447,"featureImage":1630},"\u002Fposts\u002Fwhat-is-nostr","What is Nostr?",[1447,1629,1449,1476],"relays",{"url":1453,"title":1627},{"path":1632,"title":1633,"date":1613,"tags":6,"category":1449,"featureImage":1634},"\u002Fposts\u002Fwhy-choose-privacy-focused-analytics","Why choose privacy focused Analytics",{"url":1635,"title":1633},"v1690313893\u002Fblog\u002Ffathom-header_y7s0x2",{"path":1637,"title":1638,"date":1639,"tags":6,"category":1319,"featureImage":1640},"\u002Fposts\u002Fwhat-is-api-integration","What is API Integration?","2024-06-04",{"url":1641,"title":1642},"v1719908247\u002Fapi_f3f1mh","What is API integration?",{"path":1644,"title":1645,"date":1646,"tags":1647,"category":1529,"featureImage":1649},"\u002Fposts\u002Fhow-blockchain-uses-cryptography","How Blockchain uses Cryptography","2024-05-18",[1648,1502],"blockchain",{"url":1650,"title":1651},"v1690313893\u002Fblockchain_tfrcrd","What is blockchain?",{"path":1327,"title":5,"date":1320,"tags":6,"category":1319,"featureImage":1653},{"url":1324,"title":5},{"path":1655,"title":1656,"date":1320,"tags":1657,"category":1529,"featureImage":1658},"\u002Fposts\u002Fwhat-is-blockchain","What is Blockchain",[1648,1529],{"url":1650,"title":1651},{"path":1660,"title":1661,"date":1662,"tags":1663,"category":1529,"featureImage":1665},"\u002Fposts\u002Fintroduction-to-public-and-private-key-cryptography","Public Key Cryptography Explained: A Beginner's Guide","2024-05-12",[1664,1529,1447],"encryption",{"url":1650,"title":1666},"Introduction to public and private key cryptography",{"path":1668,"title":1669,"date":1662,"tags":1670,"category":1529,"featureImage":1671},"\u002Fposts\u002Fwhat-is-bitcoin","What is Bitcoin?",[1648,1529],{"url":1531,"title":1669},{"path":1673,"title":1674,"date":1675,"tags":6,"category":1319,"featureImage":1676},"\u002Fposts\u002Fwhat-are-web-sockets","What are Web Sockets","2023-09-06",{"url":1324,"title":1677},"What are Websockets",{"path":1679,"title":1680,"date":1681,"tags":6,"category":1319,"featureImage":1682},"\u002Fposts\u002Fwhat-is-api-first","What is API First?","2023-07-13",{"url":1641,"title":1680},{"path":1684,"title":1685,"date":1686,"tags":6,"category":1319,"featureImage":1687},"\u002Fposts\u002Fapi-gateway-pattern","API Gateway Pattern","2023-06-10",{"url":1641,"title":1685},{"path":1689,"title":1690,"date":1686,"tags":6,"category":1319,"featureImage":1691},"\u002Fposts\u002Fbackend-for-frontend-design-pattern","Backend for Frontend Design Pattern",{"url":1641,"title":1690},{"path":1693,"title":1694,"date":1695,"tags":6,"category":1319,"featureImage":1696},"\u002Fposts\u002Fguide-api-development","An introductory guide to API Development","2023-06-05",{"url":1641,"title":1697},"API Development",{"path":1699,"title":1700,"date":1695,"tags":6,"category":1319,"featureImage":1701},"\u002Fposts\u002Fwhat-is-a-rest-api","What is a REST API?",{"url":1641,"title":1702},"What is an API?",{"path":1704,"title":1702,"date":1705,"tags":6,"category":1319,"featureImage":1706},"\u002Fposts\u002Fwhat-is-an-api","2023-06-01",{"url":1641,"title":1702},{"path":1708,"title":1709,"date":1705,"tags":6,"category":1319,"featureImage":1710},"\u002Fposts\u002Fwhat-is-api-development","What is API Development ?",{"url":1641,"title":1711},"What is API development?",[1713,1714],{"label":1384,"description":1385,"icon":1386,"to":1387},{"label":1389,"description":1390,"icon":1391,"to":1392},[1716,1717],{"label":1371,"icon":1372,"description":1373,"to":1374},{"label":1376,"icon":1377,"description":1378,"to":1379},[1719],{"label":1397,"icon":1398,"description":1399,"to":1400},[1721,1724,1727,1730,1733],{"path":1722,"title":1723},"\u002Flegals\u002Fcookie-policy","Cookie Policy",{"path":1725,"title":1726},"\u002Flegals\u002Fcopyright","Copyright",{"path":1728,"title":1729},"\u002Flegals\u002Fcustomer-support","Customer Support",{"path":1731,"title":1732},"\u002Flegals\u002Fprivacy-policy","Privacy Policy",{"path":1734,"title":1735},"\u002Flegals\u002Fterms-of-service","Terms of Service",1793132669336]